is one of most popular weapons of choice for managing IT infrastructure as code. And what could be better than controlling chef directly from your chat using Hubot? In this chapter we will write a custom Hubot script that will be running commands for you.
To begin the integration, you have to get knife command to work on your bot server. For that you need to install chef gem and .
You have a couple of options here, one is to make knife work from any directory by placing the configuration at /etc/chef, other is to have your *.pem and knife.rb in some directory from where knife command will work without being available globally. Our example script will be running knife from /home/hubot/knife directory.
Here is a quick example of Hubot script with one, yet very powerful chef’s knife command:
scripts/chef.coffee
1 # Description 2 # Hubot script that runs Chef's knife 3 # 4 # Commands: 5 # hubot knife <command> - execute knife command 6 # 7 # Author: 8 # spajus 9 10 module.exports = (robot) -> 11 knife_opts = { cwd: '/home/hubot/knife' } 12 cp = require 'child_process' 13 robot.respond /knife (.*)/i, (msg) -> 14 cp.exec "knife #{msg.match[1]}", knife_opts, (error, stdout, stderr) -> 15 msg.send stdout if stdout 16 msg.send "Error: #{stderr}" if stderr If your knife command works globally, you may set knife_opts to {}.
This script is written using the technique explained in “Hubot Scripting” chapter - see the calendar script in “Reacting To Messages In Chatroom” section for explanation how invoking shell commands works with Hubot scripts.
Example of this script in action:
Tomas V. hubot knife role list | grep jobs$ Hubot analytics-jobs core-jobs Yes, even pipe works. This script is so powerfull, that you MUST take precautions to secure it properly, or somebody can can do devastating things not only to hubot’s machine, but to all your servers. A good way is to execute cp.exec only when it’s invoked in devops or admins room or by small set of trusted users. See “Roles And Authentication” chapter to see how it can be done, and use it at your own risk.
While the script we’ve written is very powerful already, we may want to make our lives easier and let Hubot do the thinking. This is especially helpful if you want to expose a set of secure commands for your developers to perform actions without getting to know how chef and knife works. Behold, the advanced chef integration script, that includes hubot knife command.
scripts/chef.coffee
1 # Description 2 # Hubot script that runs Chef's knife 3 # 4 # Commands: 5 # hubot knife <command> - execute knife command (only in devops chat) 6 # hubot server list - list all our servers registered with chef 7 # hubot server list <pattern> - list our servers registered with chef match\ 8 ing a pattern 9 # hubot server search <pattern> - search for servers matching chef role (* \ 10 works) 11 # hubot servers <pattern> - search for servers matching '*-<pattern>' chef \ 12 role 13 # hubot server roles - list all chef roles 14 # hubot server roles <pattern> - list chef roles matching a pattern 15 # 16 # Author: 17 # spajus 18 19 module.exports = (robot) -> 20 21 cp = require 'child_process' 22 knife_opts = { cwd: '/home/hubot/knife' } 23 24 handle_response = (msg) -> 25 (error, stdout, stderr) -> 26 msg.send stdout if stdout 27 msg.send "Error: #{stderr}" if stderr 28 29 robot.respond /knife (.*)/i, (msg) -> 30 if msg.message.room != '<insert devops room id>' 31 msg.send "Do it in devops room please" 32 return 33 cp.exec "knife #{msg.match[1]}", 34 knife_opts, handle_response(msg) 35 36 robot.respond /server list$/i, (msg) -> 37 cp.exec "knife node list", 38 knife_opts, handle_response(msg) 39 40 robot.respond /server list (.*)/i, (msg) -> 41 cp.exec "knife node list | grep #{msg.match[1]}", 42 knife_opts, handle_response(msg) 43 44 robot.respond /server roles?$/i, (msg) -> 45 cp.exec "knife role list", 46 knife_opts, handle_response(msg) 47 48 robot.respond /server roles? (.*)/i, (msg) -> 49 cp.exec "knife role list | grep #{msg.match[1]}$", 50 knife_opts, handle_response(msg) 51 52 robot.respond /server search (.*)/i, (msg) -> 53 cp.exec "knife search node 'roles:#{msg.match[1]}' -a run_list", 54 knife_opts, handle_response(msg) 55 56 robot.respond /servers (.*)/i, (msg) -> 57 cp.exec "knife search node 'roles:*-#{msg.match[1]}' -i", 58 knife_opts, handle_response(msg) You can find this script at .
Script in action:
Tomas V. hubot help server Hubot hubot server list - list all our servers registered with chef hubot server list <pattern> - list our servers registered with chef\ matching a pattern hubot server roles - list all chef roles hubot server roles <pattern> - list chef roles matching a pattern hubot server search <pattern> - search for servers matching chef ro\ le (* works) hubot servers <pattern> - search for servers matching '*-<pattern>'\ chef role Tomas V. hubot server list indexer Hubot indexer.botserv.org Tomas V. hubot server list redis Hubot redis1.botserv.org Tomas V. hubot server list static Hubot static1.botserv.org static2.botserv.org static-x1.botserv.org static-x2.botserv.org static11.botserv.org static12.botserv.org static13.botserv.org static14.botserv.org Tomas V. hubot server roles redis Hubot core-redis redis Tomas V. hubot server search core-redis Hubot 2 items found db3.botserv.org: run_list: role[machine], role[core-redis] redis1.botserv.org: run_list: role[machine], role[redis], role[core-redis] Tomas V. hubot servers uk Hubot 14 items found indexer.botserv.org static2.botserv.org front2.botserv.org app4.botserv.org jobs9.botserv.org db4.botserv.org app1.botserv.org search2.botserv.org front1.botserv.org app2.botserv.org db1.botserv.org static1.botserv.org db2.botserv.org search1.botserv.org Tune this script and add shortcuts to your favorite knife commands.
There are Hubot scripts out there that work nearly the same. For example, this package at GitHub: . It may be a good alternative if you want something working right out of the box, however, I believe Hubot Chef integration is too important, you can’t just throw something in and expect it will suit your needs. You should write every bit of it yourself, add safety precautions, enrich it with shorthands for most often used commands. By getting very intimate with this integration script you will be sure it does all you want, and you will be able to sleep at night knowing that you built it in a way that nobody can do any harm to your infrastructure.
This book will not cover these, however you can use same techniques that were used in Chef’s integration to write yourself a custom integration with any infrastructure management tool. Just make sure you follow through the chapter and understand how chef.coffee works.