silver_spurs 1.0.2 → 2.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -17,3 +17,19 @@ test/version_tmp
17
17
  tmp
18
18
  \#*#
19
19
  /silver_spurs_async
20
+ ### /Users/cblades/.gitignore-boilerplates/Global/Emacs.gitignore
21
+
22
+ *~
23
+ \#*\#
24
+ /.emacs.desktop
25
+ /.emacs.desktop.lock
26
+ .elc
27
+ auto-save-list
28
+ tramp
29
+ .\#*
30
+
31
+ # Org-mode
32
+ .org-id-locations
33
+ *_archive
34
+
35
+
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # SilverSpurs [![Build Status](https://travis-ci.org/christian-blades-cb/silver_spurs.png?branch=master)](https://travis-ci.org/christian-blades-cb/silver_spurs)
1
+ # SilverSpurs [![Build Status](https://travis-ci.org/christian-blades-cb/silver_spurs.png?branch=master)](https://travis-ci.org/christian-blades-cb/silver_spurs) [![Gem Version](https://badge.fury.io/rb/silver_spurs.png)](http://badge.fury.io/rb/silver_spurs)
2
2
 
3
3
  RESTful Chef bootstrapping
4
4
 
@@ -2,12 +2,25 @@ require 'sinatra/base'
2
2
  require 'silver_spurs/knife_interface'
3
3
  require 'json'
4
4
  require 'silver_spurs/asyncifier'
5
+ require 'silver_spurs/chef_interface'
6
+ require 'silver_spurs/chef_exceptions'
7
+ require 'ridley'
5
8
 
6
9
  module SilverSpurs
7
10
  class App < Sinatra::Base
8
11
 
9
12
  set :deployment_key, "/etc/chef/deployment_key.pem"
10
13
  set :deployment_user, "silverspurs"
14
+ set :chef_config, {
15
+ server_url: 'http://localhost:4000',
16
+ client_name: 'silver_spurs',
17
+ client_key: '/etc/chef/silver_spurs.pem',
18
+ ssh: {
19
+ user: settings.deployment_user,
20
+ keys: [ settings.deployment_key ],
21
+ paranoid: false
22
+ }
23
+ }
11
24
  # sane setting for AD subdomain
12
25
  set :node_name_filter, /^[-A-Za-z0-9]{3,15}$/
13
26
 
@@ -83,11 +96,20 @@ module SilverSpurs
83
96
  status 550
84
97
  end
85
98
  end
86
-
99
+
100
+ post '/kick/:ip' do
101
+ run_list = params[:run] || []
102
+ chef = ChefInterface.new(settings.chef_config)
103
+ begin
104
+ chef.chef_run(params[:ip], run_list).to_json
105
+ rescue SilverSpurs::NodeNotFoundException
106
+ status 404
107
+ end
108
+ end
87
109
 
88
110
  def required_vars?(params, requirement_list)
89
111
  requirement_list.none? { |required_param| params[required_param].nil? }
90
112
  end
91
-
113
+
92
114
  end
93
115
  end
@@ -0,0 +1,4 @@
1
+ module SilverSpurs
2
+ class NodeNotFoundException < Exception
3
+ end
4
+ end
@@ -0,0 +1,36 @@
1
+ require 'singleton'
2
+ require 'forwardable'
3
+ require 'ridley'
4
+ require 'silver_spurs/chef_exceptions'
5
+
6
+ module SilverSpurs
7
+ class ChefInterface
8
+
9
+ def initialize(options)
10
+ @chef_config = options
11
+ end
12
+
13
+ def chef_run(node_name, run_list = [])
14
+ node = find_node node_name
15
+ if run_list.size > 0
16
+ command = "sudo chef-client -o '#{run_list.join(',')}'"
17
+ ridley.node.execute_command node.public_hostname, command
18
+ else
19
+ node.chef_run
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def ridley
26
+ @ridley ||= Ridley.new(@chef_config)
27
+ end
28
+
29
+ def find_node(node_name)
30
+ node = ridley.node.find(node_name)
31
+ raise NodeNotFoundException.new unless node
32
+ node
33
+ end
34
+
35
+ end
36
+ end
@@ -4,6 +4,7 @@ require 'json'
4
4
  require 'silver_spurs/knife_interface'
5
5
  require 'silver_spurs/client/exceptions'
6
6
  require 'silver_spurs/client/bootstrap_run'
7
+ require 'silver_spurs/client/chef_run'
7
8
 
8
9
  module SilverSpurs
9
10
  class Client
@@ -24,6 +25,12 @@ module SilverSpurs
24
25
  BootstrapRun.new(response.headers[:location], :timeout => @timeout)
25
26
  end
26
27
 
28
+ def start_chef_run(host_name, runlist = [])
29
+ response = spur_host["kick/#{host_name}"].post :params => { :run => runlist }
30
+ raise ClientException.new("the host name was not found", response) if response.code == 404
31
+ ChefRun.new JSON.parse(response)
32
+ end
33
+
27
34
  private
28
35
 
29
36
  def parameterize_hash(param_hash)
@@ -0,0 +1,44 @@
1
+ require 'rest-client'
2
+ require 'silver_spurs/client/exceptions'
3
+ require 'erb'
4
+
5
+ module SilverSpurs
6
+ class ChefRun
7
+
8
+ attr_reader :log, :status
9
+
10
+ def initialize(response)
11
+ @status = convert_status response
12
+ @log = prettify_log response
13
+ end
14
+
15
+ private
16
+
17
+ def convert_status(response)
18
+ code = response[0]
19
+ case code
20
+ when 'ok'
21
+ :success
22
+ when 'error'
23
+ :failed
24
+ end
25
+ end
26
+
27
+ def prettify_log(response)
28
+ run_info = response[1]
29
+ stdout = run_info['stdout']
30
+ stderr = run_info['stderr']
31
+ exit_code = run_info['exit_code'] || run_info['exit_status']
32
+
33
+ template = ERB.new <<-END
34
+ Exit Code: <%= exit_code %>
35
+ --STDOUT-----------------
36
+ <%= stdout %>
37
+ --STDERR-----------------
38
+ <%= stderr %>
39
+ END
40
+ template.result binding
41
+ end
42
+
43
+ end
44
+ end
@@ -1,3 +1,3 @@
1
1
  module SilverSpurs
2
- VERSION = "1.0.2"
2
+ VERSION = "2.0.0.rc1"
3
3
  end
data/silver_spurs.gemspec CHANGED
@@ -19,6 +19,7 @@ Gem::Specification.new do |gem|
19
19
  gem.add_dependency 'chef', '>= 10.18.2'
20
20
  gem.add_dependency 'rest-client', '~> 1.6.7'
21
21
  gem.add_dependency 'addressable', '~> 2.3.3'
22
+ gem.add_dependency "ridley", "~> 0.11.0.rc1"
22
23
 
23
24
  gem.add_development_dependency 'guard'
24
25
  gem.add_development_dependency 'guard-rspec'
@@ -269,6 +269,29 @@ describe SilverSpurs::App do
269
269
  end
270
270
 
271
271
  end
272
+
273
+ describe '/kick/:ip' do
274
+ before :each do
275
+ @chef = double('chef')
276
+ SilverSpurs::ChefInterface.stub(:new).and_return @chef
277
+ end
278
+
279
+ it 'starts a chef run' do
280
+ @chef.should_receive :chef_run
281
+
282
+ post '/kick/node'
283
+ end
284
+
285
+ context 'when the node does not exist' do
286
+ it 'should return a 404' do
287
+ @chef.stub(:chef_run).and_raise(SilverSpurs::NodeNotFoundException.new)
288
+
289
+ post '/kick/node'
290
+ last_response.status.should eq 404
291
+ end
292
+ end
293
+
294
+ end
272
295
 
273
296
  end
274
297
 
@@ -0,0 +1,135 @@
1
+ require 'spec_helper'
2
+ require 'ridley'
3
+
4
+ describe SilverSpurs::ChefInterface do
5
+
6
+ describe :chef_run do
7
+
8
+ before :each do
9
+ @chef_i = SilverSpurs::ChefInterface.new({})
10
+ @ridley = double('ridley')
11
+ @node_resource = double('node_resource')
12
+ @node_obj = double('node_obj')
13
+
14
+ @chef_i.stub(:ridley).and_return @ridley
15
+ @ridley.stub(:node).and_return @node_resource
16
+ end
17
+
18
+ it 'finds a node and then launches a chef run' do
19
+ @chef_i.should_receive(:find_node).and_return @node_obj
20
+ @node_obj.should_receive(:chef_run)
21
+
22
+ @chef_i.chef_run 'node'
23
+ end
24
+
25
+ context 'with a run list' do
26
+ before :each do
27
+ @chef_i.stub(:find_node).and_return @node_obj
28
+ end
29
+
30
+ after :each do
31
+ @chef_i.chef_run 'node_name', ['recipe[one]', 'recipe[two]']
32
+ end
33
+
34
+ it 'should call execute off of the node resource' do
35
+ @node_obj.stub(:public_hostname).and_return 'hostname'
36
+ @node_resource.should_receive(:execute_command)
37
+ end
38
+
39
+ it 'should pass in the host name from the node object' do
40
+ @node_obj.should_receive(:public_hostname).and_return 'hostname'
41
+ @node_resource.should_receive(:execute_command).with('hostname', anything)
42
+ end
43
+
44
+ it 'should pass in the run list as a comma-seperated string' do
45
+ @node_obj.stub(:public_hostname).and_return 'hostname'
46
+ @node_resource.should_receive(:execute_command).with(anything, %r{'recipe\[one\],recipe\[two\]'})
47
+ end
48
+ end
49
+
50
+ context 'without a run list' do
51
+ after :each do
52
+ @chef_i.chef_run 'node_name'
53
+ end
54
+
55
+ before :each do
56
+ @chef_i.stub(:find_node).and_return @node_obj
57
+ end
58
+
59
+ it 'should call chef_run off of the node object' do
60
+ @node_obj.should_receive :chef_run
61
+ end
62
+ end
63
+
64
+ end
65
+
66
+ describe :find_node do
67
+
68
+ before :each do
69
+ @chef_i = SilverSpurs::ChefInterface.new({})
70
+ @ridley = double('ridley')
71
+ @node_resource = double('node_resource')
72
+
73
+ @chef_i.stub(:ridley).and_return @ridley
74
+ @ridley.stub(:node).and_return @node_resource
75
+ end
76
+
77
+ def call_find_node
78
+ @chef_i.send(:find_node, 'node_name')
79
+ end
80
+
81
+ it 'asks ridley for the node' do
82
+ @node_resource.should_receive(:find).with('node_name').and_return double('node_obj')
83
+ call_find_node
84
+ end
85
+
86
+ context 'when node exists' do
87
+ before :each do
88
+ @node_obj = double('node_obj')
89
+ @node_resource.stub(:find).and_return @node_obj
90
+ end
91
+
92
+ it 'should return the node' do
93
+ call_find_node.should eq @node_obj
94
+ end
95
+ end
96
+
97
+ context 'when node does not exist' do
98
+ before :each do
99
+ @node_resource.stub(:find).and_return nil
100
+ end
101
+
102
+ it 'should throw an error' do
103
+ expect { call_find_node }.to raise_error
104
+ end
105
+ end
106
+
107
+ end
108
+
109
+ describe :ridley do
110
+ before :each do
111
+ @chef_i = SilverSpurs::ChefInterface.new({})
112
+ end
113
+
114
+ def call_ridley
115
+ @chef_i.send :ridley
116
+ end
117
+
118
+ context 'when @ridley has not been initialized' do
119
+ it 'creates a new Ridley instance' do
120
+ Ridley.should_receive(:new)
121
+ call_ridley
122
+ end
123
+ end
124
+
125
+ context 'when @ridley has been initialized' do
126
+ it 're-uses the old ridley' do
127
+ Ridley.should_receive(:new).once.and_return double('ridley')
128
+ call_ridley
129
+ call_ridley
130
+ end
131
+ end
132
+
133
+ end
134
+
135
+ end
@@ -0,0 +1,84 @@
1
+ require 'silver_spurs/client/chef_run'
2
+
3
+ describe SilverSpurs::ChefRun do
4
+ describe :convert_status do
5
+ before :each do
6
+ @chef_run = SilverSpurs::ChefRun.new(["ok",{'stdout' => '', 'stderr' => '', 'exit_code' => 0, 'exit_status' => 0}])
7
+ end
8
+
9
+ def call_convert_status(response)
10
+ @chef_run.send(:convert_status, response)
11
+ end
12
+
13
+ context 'response is "ok"' do
14
+ before :each do
15
+ @response = ["ok", {}]
16
+ end
17
+
18
+ it "should return :success" do
19
+ call_convert_status(@response).should eq :success
20
+ end
21
+ end
22
+
23
+ context 'response is "error"' do
24
+ before :each do
25
+ @response = ["error", {}]
26
+ end
27
+
28
+ it "should return :failed" do
29
+ call_convert_status(@response).should eq :failed
30
+ end
31
+ end
32
+
33
+ end
34
+
35
+ describe :prettify_log do
36
+ before :each do
37
+ @chef_run = SilverSpurs::ChefRun.new(["ok",{'stdout' => '', 'stderr' => '', 'exit_code' => 0, 'exit_status' => 0}])
38
+ end
39
+
40
+ def call_prettify_log(response)
41
+ @chef_run.send :prettify_log, response
42
+ end
43
+
44
+ it 'should output stderr' do
45
+ response = ["ok",{'stdout' => '', 'stderr' => 'stdERR, son', 'exit_code' => 0, 'exit_status' => 0}]
46
+ call_prettify_log(response).should match(/stdERR, son/)
47
+ end
48
+
49
+ it 'should output stdout' do
50
+ response = ["ok",{'stdout' => 'stdOUT, son', 'stderr' => '', 'exit_code' => 0, 'exit_status' => 0}]
51
+ call_prettify_log(response).should match(/stdOUT, son/)
52
+ end
53
+
54
+ context 'when exit_code and exit_status are present' do
55
+ it 'should prefer exit_code' do
56
+ response = ["ok",{'stdout' => '', 'stderr' => '', 'exit_code' => 1, 'exit_status' => 2}]
57
+ call_prettify_log(response).should match(/Exit Code: 1/)
58
+ end
59
+ end
60
+
61
+ context 'when exit_code is not present' do
62
+ it 'should use exit_status' do
63
+ response = ["ok",{'stdout' => '', 'stderr' => '', 'exit_code' => nil, 'exit_status' => 2}]
64
+ call_prettify_log(response).should match(/Exit Code: 2/)
65
+ end
66
+ end
67
+
68
+ context 'when exit_status is not present' do
69
+ it 'should use exit_code' do
70
+ response = ["ok",{'stdout' => '', 'stderr' => '', 'exit_code' => 1, 'exit_status' => nil}]
71
+ call_prettify_log(response).should match(/Exit Code: 1/)
72
+ end
73
+ end
74
+
75
+ context 'when exit_code and exit_status are not present' do
76
+ it 'should use nil' do
77
+ response = ["ok",{'stdout' => '', 'stderr' => '', 'exit_code' => nil, 'exit_status' => nil}]
78
+ call_prettify_log(response).should match(/Exit Code: /)
79
+ end
80
+ end
81
+
82
+ end
83
+
84
+ end
@@ -109,5 +109,28 @@ describe SilverSpurs::Client do
109
109
  end
110
110
 
111
111
  end
112
+
113
+ describe :start_chef_run do
114
+ before :each do
115
+ @client = SilverSpurs::Client.new 'http://localhost'
116
+ @resource = double('rest-resource')
117
+ @resource.stub(:[]).and_return @resource
118
+ @client.stub(:spur_host).and_return @resource
119
+ end
120
+
121
+ it 'returns a ChefRun object' do
122
+ response_payload = ["ok", {'stderr' => '', 'stdout' => '', 'exit_code' => 0, 'exit_status' => 0}]
123
+ response = double('response')
124
+ response.stub(:to_str).and_return response_payload.to_json
125
+ response.stub(:code).and_return 200
126
+
127
+ @resource.stub(:post).and_return response
128
+
129
+ SilverSpurs::ChefRun.should_receive(:new).with(response_payload)
130
+
131
+ @client.start_chef_run('hostname')
132
+ end
133
+
134
+ end
112
135
 
113
136
  end
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: silver_spurs
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
5
- prerelease:
4
+ version: 2.0.0.rc1
5
+ prerelease: 6
6
6
  platform: ruby
7
7
  authors:
8
8
  - Christian Blades
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-04-01 00:00:00.000000000 Z
12
+ date: 2013-05-07 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: sinatra
@@ -75,6 +75,22 @@ dependencies:
75
75
  - - ~>
76
76
  - !ruby/object:Gem::Version
77
77
  version: 2.3.3
78
+ - !ruby/object:Gem::Dependency
79
+ name: ridley
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ~>
84
+ - !ruby/object:Gem::Version
85
+ version: 0.11.0.rc1
86
+ type: :runtime
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ~>
92
+ - !ruby/object:Gem::Version
93
+ version: 0.11.0.rc1
78
94
  - !ruby/object:Gem::Dependency
79
95
  name: guard
80
96
  requirement: !ruby/object:Gem::Requirement
@@ -207,15 +223,20 @@ files:
207
223
  - lib/silver_spurs.rb
208
224
  - lib/silver_spurs/app.rb
209
225
  - lib/silver_spurs/asyncifier.rb
226
+ - lib/silver_spurs/chef_exceptions.rb
227
+ - lib/silver_spurs/chef_interface.rb
210
228
  - lib/silver_spurs/client.rb
211
229
  - lib/silver_spurs/client/bootstrap_run.rb
230
+ - lib/silver_spurs/client/chef_run.rb
212
231
  - lib/silver_spurs/client/exceptions.rb
213
232
  - lib/silver_spurs/knife_interface.rb
214
233
  - lib/silver_spurs/version.rb
215
234
  - silver_spurs.gemspec
216
235
  - spec/lib/silver_spurs/app_spec.rb
217
236
  - spec/lib/silver_spurs/asyncifier_spec.rb
237
+ - spec/lib/silver_spurs/chef_interface_spec.rb
218
238
  - spec/lib/silver_spurs/client/bootstrap_run_spec.rb
239
+ - spec/lib/silver_spurs/client/chef_run_spec.rb
219
240
  - spec/lib/silver_spurs/client_spec.rb
220
241
  - spec/lib/silver_spurs/knife_interface_spec.rb
221
242
  - spec/spec_helper.rb
@@ -231,28 +252,24 @@ required_ruby_version: !ruby/object:Gem::Requirement
231
252
  - - ! '>='
232
253
  - !ruby/object:Gem::Version
233
254
  version: '0'
234
- segments:
235
- - 0
236
- hash: 1092412529978158920
237
255
  required_rubygems_version: !ruby/object:Gem::Requirement
238
256
  none: false
239
257
  requirements:
240
- - - ! '>='
258
+ - - ! '>'
241
259
  - !ruby/object:Gem::Version
242
- version: '0'
243
- segments:
244
- - 0
245
- hash: 1092412529978158920
260
+ version: 1.3.1
246
261
  requirements: []
247
262
  rubyforge_project:
248
- rubygems_version: 1.8.25
263
+ rubygems_version: 1.8.23
249
264
  signing_key:
250
265
  specification_version: 3
251
266
  summary: RESTful service to kick off chef bootstraps
252
267
  test_files:
253
268
  - spec/lib/silver_spurs/app_spec.rb
254
269
  - spec/lib/silver_spurs/asyncifier_spec.rb
270
+ - spec/lib/silver_spurs/chef_interface_spec.rb
255
271
  - spec/lib/silver_spurs/client/bootstrap_run_spec.rb
272
+ - spec/lib/silver_spurs/client/chef_run_spec.rb
256
273
  - spec/lib/silver_spurs/client_spec.rb
257
274
  - spec/lib/silver_spurs/knife_interface_spec.rb
258
275
  - spec/spec_helper.rb