silver_spurs 1.0.2 → 2.0.0.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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