Skip to content
This repository has been archived by the owner on Feb 9, 2018. It is now read-only.

Commit

Permalink
Merge pull request #7 from christian-blades-cb/kick_start
Browse files Browse the repository at this point in the history
Support ad-hoc chef runs
  • Loading branch information
tkling committed May 6, 2013
2 parents c222d15 + c9497dc commit 2b4d40f
Show file tree
Hide file tree
Showing 12 changed files with 398 additions and 3 deletions.
16 changes: 16 additions & 0 deletions .gitignore
Expand Up @@ -17,3 +17,19 @@ test/version_tmp
tmp
\#*#
/silver_spurs_async
### /Users/cblades/.gitignore-boilerplates/Global/Emacs.gitignore

*~
\#*\#
/.emacs.desktop
/.emacs.desktop.lock
.elc
auto-save-list
tramp
.\#*

# Org-mode
.org-id-locations
*_archive


26 changes: 24 additions & 2 deletions lib/silver_spurs/app.rb
Expand Up @@ -2,12 +2,25 @@
require 'silver_spurs/knife_interface'
require 'json'
require 'silver_spurs/asyncifier'
require 'silver_spurs/chef_interface'
require 'silver_spurs/chef_exceptions'
require 'ridley'

module SilverSpurs
class App < Sinatra::Base

set :deployment_key, "/etc/chef/deployment_key.pem"
set :deployment_user, "silverspurs"
set :chef_config, {
server_url: 'http://localhost:4000',
client_name: 'silver_spurs',
client_key: '/etc/chef/silver_spurs.pem',
ssh: {
user: settings.deployment_user,
keys: [ settings.deployment_key ],
paranoid: false
}
}
# sane setting for AD subdomain
set :node_name_filter, /^[-A-Za-z0-9]{3,15}$/

Expand Down Expand Up @@ -83,11 +96,20 @@ class App < Sinatra::Base
status 550
end
end


post '/kick/:ip' do
run_list = params[:run] || []
chef = ChefInterface.new(settings.chef_config)
begin
chef.chef_run(params[:ip], run_list).to_json
rescue SilverSpurs::NodeNotFoundException
status 404
end
end

def required_vars?(params, requirement_list)
requirement_list.none? { |required_param| params[required_param].nil? }
end

end
end
4 changes: 4 additions & 0 deletions lib/silver_spurs/chef_exceptions.rb
@@ -0,0 +1,4 @@
module SilverSpurs
class NodeNotFoundException < Exception
end
end
36 changes: 36 additions & 0 deletions lib/silver_spurs/chef_interface.rb
@@ -0,0 +1,36 @@
require 'singleton'
require 'forwardable'
require 'ridley'
require 'silver_spurs/chef_exceptions'

module SilverSpurs
class ChefInterface

def initialize(options)
@chef_config = options
end

def chef_run(node_name, run_list = [])
node = find_node node_name
if run_list.size > 0
command = "sudo chef-client -o '#{run_list.join(',')}'"
ridley.node.execute_command node.public_hostname, command
else
node.chef_run
end
end

private

def ridley
@ridley ||= Ridley.new(@chef_config)
end

def find_node(node_name)
node = ridley.node.find(node_name)
raise NodeNotFoundException.new unless node
node
end

end
end
7 changes: 7 additions & 0 deletions lib/silver_spurs/client.rb
Expand Up @@ -4,6 +4,7 @@
require 'silver_spurs/knife_interface'
require 'silver_spurs/client/exceptions'
require 'silver_spurs/client/bootstrap_run'
require 'silver_spurs/client/chef_run'

module SilverSpurs
class Client
Expand All @@ -24,6 +25,12 @@ def start_bootstrap(ip, node_name, options = {})
BootstrapRun.new(response.headers[:location], :timeout => @timeout)
end

def start_chef_run(host_name, runlist = [])
response = spur_host["kick/#{host_name}"].post :params => { :run => runlist }
raise ClientException.new("the host name was not found", response) if response.code == 404
ChefRun.new JSON.parse(response)
end

private

def parameterize_hash(param_hash)
Expand Down
44 changes: 44 additions & 0 deletions lib/silver_spurs/client/chef_run.rb
@@ -0,0 +1,44 @@
require 'rest-client'
require 'silver_spurs/client/exceptions'
require 'erb'

module SilverSpurs
class ChefRun

attr_reader :log, :status

def initialize(response)
@status = convert_status response
@log = prettify_log response
end

private

def convert_status(response)
code = response[0]
case code
when 'ok'
:success
when 'error'
:failed
end
end

def prettify_log(response)
run_info = response[1]
stdout = run_info['stdout']
stderr = run_info['stderr']
exit_code = run_info['exit_code'] || run_info['exit_status']

template = ERB.new <<-END
Exit Code: <%= exit_code %>
--STDOUT-----------------
<%= stdout %>
--STDERR-----------------
<%= stderr %>
END
template.result binding
end

end
end
2 changes: 1 addition & 1 deletion lib/silver_spurs/version.rb
@@ -1,3 +1,3 @@
module SilverSpurs
VERSION = "1.0.2"
VERSION = "2.0.0.rc1"
end
1 change: 1 addition & 0 deletions silver_spurs.gemspec
Expand Up @@ -19,6 +19,7 @@ Gem::Specification.new do |gem|
gem.add_dependency 'chef', '>= 10.18.2'
gem.add_dependency 'rest-client', '~> 1.6.7'
gem.add_dependency 'addressable', '~> 2.3.3'
gem.add_dependency "ridley", "~> 0.11.0.rc1"

gem.add_development_dependency 'guard'
gem.add_development_dependency 'guard-rspec'
Expand Down
23 changes: 23 additions & 0 deletions spec/lib/silver_spurs/app_spec.rb
Expand Up @@ -269,6 +269,29 @@ def app
end

end

describe '/kick/:ip' do
before :each do
@chef = double('chef')
SilverSpurs::ChefInterface.stub(:new).and_return @chef
end

it 'starts a chef run' do
@chef.should_receive :chef_run

post '/kick/node'
end

context 'when the node does not exist' do
it 'should return a 404' do
@chef.stub(:chef_run).and_raise(SilverSpurs::NodeNotFoundException.new)

post '/kick/node'
last_response.status.should eq 404
end
end

end

end

Expand Down
135 changes: 135 additions & 0 deletions spec/lib/silver_spurs/chef_interface_spec.rb
@@ -0,0 +1,135 @@
require 'spec_helper'
require 'ridley'

describe SilverSpurs::ChefInterface do

describe :chef_run do

before :each do
@chef_i = SilverSpurs::ChefInterface.new({})
@ridley = double('ridley')
@node_resource = double('node_resource')
@node_obj = double('node_obj')

@chef_i.stub(:ridley).and_return @ridley
@ridley.stub(:node).and_return @node_resource
end

it 'finds a node and then launches a chef run' do
@chef_i.should_receive(:find_node).and_return @node_obj
@node_obj.should_receive(:chef_run)

@chef_i.chef_run 'node'
end

context 'with a run list' do
before :each do
@chef_i.stub(:find_node).and_return @node_obj
end

after :each do
@chef_i.chef_run 'node_name', ['recipe[one]', 'recipe[two]']
end

it 'should call execute off of the node resource' do
@node_obj.stub(:public_hostname).and_return 'hostname'
@node_resource.should_receive(:execute_command)
end

it 'should pass in the host name from the node object' do
@node_obj.should_receive(:public_hostname).and_return 'hostname'
@node_resource.should_receive(:execute_command).with('hostname', anything)
end

it 'should pass in the run list as a comma-seperated string' do
@node_obj.stub(:public_hostname).and_return 'hostname'
@node_resource.should_receive(:execute_command).with(anything, %r{'recipe\[one\],recipe\[two\]'})
end
end

context 'without a run list' do
after :each do
@chef_i.chef_run 'node_name'
end

before :each do
@chef_i.stub(:find_node).and_return @node_obj
end

it 'should call chef_run off of the node object' do
@node_obj.should_receive :chef_run
end
end

end

describe :find_node do

before :each do
@chef_i = SilverSpurs::ChefInterface.new({})
@ridley = double('ridley')
@node_resource = double('node_resource')

@chef_i.stub(:ridley).and_return @ridley
@ridley.stub(:node).and_return @node_resource
end

def call_find_node
@chef_i.send(:find_node, 'node_name')
end

it 'asks ridley for the node' do
@node_resource.should_receive(:find).with('node_name').and_return double('node_obj')
call_find_node
end

context 'when node exists' do
before :each do
@node_obj = double('node_obj')
@node_resource.stub(:find).and_return @node_obj
end

it 'should return the node' do
call_find_node.should eq @node_obj
end
end

context 'when node does not exist' do
before :each do
@node_resource.stub(:find).and_return nil
end

it 'should throw an error' do
expect { call_find_node }.to raise_error
end
end

end

describe :ridley do
before :each do
@chef_i = SilverSpurs::ChefInterface.new({})
end

def call_ridley
@chef_i.send :ridley
end

context 'when @ridley has not been initialized' do
it 'creates a new Ridley instance' do
Ridley.should_receive(:new)
call_ridley
end
end

context 'when @ridley has been initialized' do
it 're-uses the old ridley' do
Ridley.should_receive(:new).once.and_return double('ridley')
call_ridley
call_ridley
end
end

end

end

0 comments on commit 2b4d40f

Please sign in to comment.