Skip to content

Instantly share code, notes, and snippets.

@danielsdeleo
Created February 13, 2011 05:15
Show Gist options
  • Save danielsdeleo/824468 to your computer and use it in GitHub Desktop.
Save danielsdeleo/824468 to your computer and use it in GitHub Desktop.
#!/usr/bin/env ruby
require 'benchmark'
require 'optparse'
require 'pp'
require 'rubygems'
require 'yajl'
require 'chef/rest'
module CGrep
class Config
class Adapter
def initialize(file)
@file = file
@config = {}
end
def parse
original_verbose = $VERBOSE
$VERBOSE = nil
instance_eval(IO.read(@file))
$VERBOSE = original_verbose
@config
end
def method_missing(m,*args, &block)
if args.empty?
@config[m]
elsif
args.size == 1
@config[m] = args.first
else
super
end
end
end
def find_config
dir_specific = File.expand_path('.chef/knife.rb')
home_config = File.expand_path('~/.chef/knife.rb')
if File.exist?(dir_specific)
dir_specific
elsif File.exist?(home_config)
home_config
else
raise "No config file available"
end
end
def apply_config_file
file = @config_file || find_config
Adapter.new(file).parse.each do |key,val|
setter = "#{key}=".to_sym
send(setter, val) if respond_to?(setter)
end
end
attr_accessor :info
attr_accessor :config_file
attr_accessor :chef_server_url
attr_accessor :client_key
attr_accessor :node_name
end
module CLI
@config = Config.new
@option_parser = OptionParser.new do |o|
o.banner = 'Usage: cgrep [OPTIONS] QUERY'
o.on('-I', '--info', 'include ID, IP, tags, and roles in the output') do
@config.info = true
end
o.on('-c', '--config FILE', 'knife config file to use') do |c|
@config.config_file = c
end
o.on_tail('-h', '--help') do
puts o
exit 1
end
end
def self.config
@config
end
def self.parse!(argv)
@option_parser.parse!(argv)
end
end
class Fuzzyizer
attr_reader :query_string
alias :q :query_string
def initialize(query_string)
@query_string = query_string
end
def fuzzy_q
"tags:*#{q}* OR roles:*#{q}* OR fqdn:*#{q}* OR addresses:*#{q}*"
end
def escaped_q
URI.escape(fuzzy_q)
end
end
class ApiRequest < Chef::REST
def search(what, query)
noinflate_request(:GET, "/search/#{what}?q=#{query}")
end
def noinflate_request(method, url, headers={})
url = URI.parse("#{@url}/#{url}")
headers = build_headers(method, url, headers, nil)
retriable_rest_request(method, url, nil, headers) do |rest_request|
response = rest_request.call {|r| r.read_body}
if response.kind_of?(Net::HTTPSuccess)
if response['content-type'] =~ /json/
x = Yajl::Parser.parse(response.body.chomp)
else
response.body
end
else
if response['content-type'] =~ /json/
exception = Yajl::Parser.parse(response.body)
msg = "HTTP Request Returned #{response.code} #{response.message}: "
msg << (exception["error"].respond_to?(:join) ? exception["error"].join(", ") : exception["error"].to_s)
STDERR.puts(msg)
end
response.error!
end
end
end
end
class NodeFormatter
def initialize(node, config)
@node = node
@config = config
end
def to_s
buf = ''
buf << @node["automatic"]["fqdn"]
if @config.info
buf << "\n"
buf << " Name: #{@node['name']}\n"
buf << " IP: #{@node['automatic']['ipaddress']}\n"
buf << " Tags: #{@node["normal"]['tags'].uniq.sort.join(',')}\n" unless @node['normal']['tags'].empty?
buf << " Roles: #{@node['automatic']['roles'].sort.join(',')}\n"
end
buf
end
end
class Search
def initialize(argv)
@query = CLI.parse!(argv).first
@config = CLI.config
end
def fuzzy_q
Fuzzyizer.new(@query).escaped_q
end
def run
@config.apply_config_file
require 'benchmark'
hits = ApiRequest.new(@config.chef_server_url, @config.node_name, @config.client_key).search(:node, fuzzy_q)["rows"]
hits.sort! {|n1,n2| n1["automatic"]["fqdn"] <=> n2["automatic"]["fqdn"]}
hits.each {|n| puts NodeFormatter.new(n, @config)}
end
end
end
unless defined?(Object::Ohai)
Ohai = Module.new
Ohai::Config = Hash.new {|h,k| h[k] = []}
end
#Chef::Knife.new([]).configure_chef
CGrep::Search.new(ARGV).run
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment