knife
began as a simple thor script
created by Joshua Sierles of 37signals.
As soon as we saw it, we knew it was such a great idea that we had to
incorporate it into Chef. With a bit of a redesign, Chef 0.8 shipped
with knife
fully integrated, and it’s since become the primary
interface to Chef for many users.
In Chef 0.10, we’ve completed two long-awaited improvements to knife
:
support for custom commands and improved output formatting.
Knife Plugins
A plugin system for knife is something we’ve had on the roadmap for a
long time. Luckily for you, Ryan Davis and
Eric Hodel decided to fast-forward the
roadmap by implementing the feature themselves. With Chef 0.10, knife
will load commands from the following locations:
- The core set of knife commands shipped with Chef
- Commands in your home chef directory:
~/.chef/plugins/knife/
- Commands in a
.chef/plugins/knife/
directory in your cookbook repo - Commands located in a
chef/knife/
directory in a Ruby Gem you have
installed.
This allows you to conveniently keep a set of knife plugins that you
reuse across projects in your home directory, share plugins with your
team by including them in your cookbook repo, and share plugins with the
whole Chef community by distributing them as Ruby gems.
Writing Knife Plugins
Lets get a taste of what we can do with knife plugins by writing a
simple one. If you’re like me, you use knife search node
very frequently
and you would like to do a little less typing when searching for nodes
by Role or tag. We’ll solve the problem by creating a knife grep
command that will search for our nodes by role, tag, fqdn, or IP
address. The code for this plugin is below. To use it, simply copy the
code to ~/.chef/plugins/knife/grep.rb
Knife Grep: The Code
[sourcecode lang=”ruby”]
require ‘chef/knife’
module Kallistec
class Grep < Chef::Knife
deps do
require ‘chef/search/query’
require ‘chef/knife/search’
end
banner "knife grep QUERY"
def run
unless @query = name_args.first
ui.error "You need to specify a query term"
exit 1
end
fuzzier_query = "tags:*#{@query}* OR roles:*#{@query}* OR fqdn:*#{@query}* OR addresses:*#{@query}*"
knife_search = Chef::Knife::Search.new
knife_search.name_args = [‘node’, fuzzier_query]
knife_search.run
end
end
end
[/sourcecode]
Knife Grep: The How
For complete documentation on writing knife plugins, head on over to the
wiki. We’ll cover
the highlights here.
We start by placing the code in a namespace:
[sourcecode lang=”ruby”]
module Kallistec
end
[/sourcecode]
I’m using my nick as the namespace, but you can use whatever you want.
Next, we create a new knife command class by subclassing Chef::Knife
:
[sourcecode lang=”ruby”]
class Grep < Chef::Knife
end
[/sourcecode]
Subclassing Chef::Knife
is important, as this lets knife know you’ve
created a new command.
Next, we add our dependency to knife’s lazy loading system:
[sourcecode lang=”ruby”]
deps do
require ‘chef/knife/search’
end
[/sourcecode]
We could have declared all of our dependencies at the top of the file,
but this makes knife load more slowly for all commands, so it’s
recommended to use the lazy loader.
The heart of our knife plugin is the run
method. This method will be
called automatically after knife has parsed the command line arguments
and options. Command line arguments are made available through thename_args
Array. In the grep plugin, we simply take the first argument
and use it to generate a search query. We then run a knife search
command via knife’s internal API to perform the query and print results:
[sourcecode lang=”ruby”]
# Create a search query from user input. For input ‘chef’, this is
# equivalent to running
# `knife search node ‘tags:*chef* OR roles:*chef* OR fqdn:*chef* OR addresses:*chef*’
#
fuzzier_query = "tags:*#{@query}* OR roles:*#{@query}* OR fqdn:*#{@query}* OR addresses:*#{@query}*"
# Create a knife search command
knife_search = Chef::Knife::Search.new
# set the command line arguments
knife_search.name_args = [‘node’, fuzzier_query]
# run it!
knife_search.run
[/sourcecode]
Knife Grep: The Grepping
We can then use our new plugin like this:
dan@laptop$ knife grep ghost
1 items found
Node Name: ghost.local
Environment: production
FQDN: ghost.local
IP: 172.16.185.135
Run List: recipe[tmux]
Roles:
Recipes tmux
Platform: ubuntu 10.04
Cloud Commands are now Plugins
In order to reduce the commands shipped with knife to only those useful
to everyone, we’ve removed the cloud computing commands from core and
made them available as plugins:
In addition to these, we have new functionality available in two new
plugins:
All of these plugins are available as gems; you can install them via:
(sudo) gem install knife-ec2 # or whichever plugin you want
Prettier Knife Output
knife
‘s default JSON output has been a double edged, er, knife. While
it’s great for integrating with other tools, it isn’t optimal for human
consumption. So in 0.10 we’ve added a new “summary” output format and
made it the default. You already got a taste of the summary output above
when we demoed the knife grep
command. knife node show
produces the
same output:
dan@laptop$ knife node show ghost.local
Node Name: ghost.local
Environment: production
FQDN: ghost.local
IP: 172.16.185.135
Run List: recipe[tmux]
Roles:
Recipes tmux
Platform: ubuntu 10.04
Data bag items look pretty nifty as well:
dan@laptop$ knife data bag show users-example charles
id: charles
key: -----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAw8PkvRWOVONSByxseLhKrOH9EigRizutlfwk0/LwUvnM4Ffb
HcZ4lD1LWzewfnQsNLv6+gdhMk7gcRTFxeNLf9+//VsN5vODcLdDdd3DGKrJ6uvx
N5hs1t8fHf6B5N1Z823CsTBAspbAGqkeoiJVDfk9LKU/W+YM+GS0h2pp88H9Y9p4
ilwu55xXSH1/RKunniTvQWEdDcs/zpk5+EKoaBoaZCpItMLKMk7RziaAkYNfwiWr
6zhUpjyUbhg6giO4TKFy0HKODejFhlz4i9syYxh3fSKr+2HiUruT1Mw2RrdyFXiI
too long, didn't read...
3T8+Tb6UjZ0hMSSVsfeUlwKBgGqfvp5CQsub1HIv1YZMnZYMMDGFQm5B6CxE0Eal
frFUtlNszk2PDjCY2IWaNCyrGCVhP/ra+y+5PzO1utrob/KiRuX9D8fs3yIClsQd
2R8EomzZq554+W9UcMBrU90PTdgrMvXMGHsR/6j0hEqYT1ILje+neY6eBiiv5mPh
4vSpAoGBAKcuAifcR62UxKklaE5d6QSvq6r3OlxIJ6R/LL2yMpPkbMvaEpzN6lpH
oF3GSz78K8wPr/1Zhhm/g/VJyZA9E0n67HS+SRyTHrSCHXS9PrBGQJiV+AMvF5gf
uFP1Kv0FYJ1VS1FgFPDSTUMFTwzPvj0G8PnJi4kHcEePgpUk/ost
-----END RSA PRIVATE KEY-----
shell: zsh
There are many more examples I could show you, but you really need to
use knife in 0.10 for a while to get the full effect.
Help!
For the final touch, we’ve made the manual pages accessible via knife
, and greatly expanded the content. We now have a manual page for
help
each command category, in addition to one for general knife usage. To get
started, run knife help list
to get a list of help topics, or runknife help knife
for general knife documentation. By the way,
improving documentation is an easy way to get started contributing to
Chef, so if you see something in the docs that you’d like to improve,
dive right in—the manpages are written in friendly
markdown
and generated with ronn.
Try it Today!
If you’re using the Opscode
Platform, you can start using
the new knife right now by installing the 0.10 Release Candidate to your
laptop/workstation:
(sudo) gem install chef --pre
If you run into any bugs, you can seemlessly downgrade via gem
(don’t forget to file a bug
uninstall
report though!)
If you’re running you’re own server, you’ll need to upgrade the server
before you can start using the new knife. You can install a 0.10 server
for testing by bootstrapping with chef-solo:
[sourcecode lang=”text”]
sudo chef-solo -j chef.json -c solo.rb -r https://s3.amazonaws.com/chef-solo/bootstrap-0.10.0.rc.0.tar.gz
[/sourcecode]
You can also find upgrade instructions on the
wiki.,
and, as always, feel free to ask for help on our mailing
list and on our IRC channel
(irc.freenode.net#chef).