appforce-spawn 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +7 -0
  2. data/.appforce.example +4 -0
  3. data/.gitignore +8 -0
  4. data/Gemfile +17 -0
  5. data/History +85 -0
  6. data/README.md +144 -0
  7. data/ansible/README.md +31 -0
  8. data/ansible/common.yml +6 -0
  9. data/ansible/roles/common/tasks/.keep +0 -0
  10. data/ansible/roles/common/tasks/active_users.yml +23 -0
  11. data/ansible/roles/common/tasks/groups.yml +28 -0
  12. data/ansible/roles/common/tasks/inactive_users.yml +26 -0
  13. data/ansible/roles/common/tasks/main.yml +5 -0
  14. data/ansible/roles/common/tasks/setup.yml +6 -0
  15. data/ansible/roles/scout/tasks/.keep +0 -0
  16. data/ansible/roles/scout/tasks/install.yml +28 -0
  17. data/ansible/roles/scout/tasks/main.yml +2 -0
  18. data/ansible/roles/scout/vars/.keep +0 -0
  19. data/ansible/rvm.yml +13 -0
  20. data/ansible/scout.yml +6 -0
  21. data/ansible/site.yml +4 -0
  22. data/appforce-spawn.gemspec +24 -0
  23. data/bin/appforce-spawn +161 -0
  24. data/lib/appforce-spawn.rb +1 -0
  25. data/lib/appforce/config.rb +50 -0
  26. data/lib/appforce/logger.rb +25 -0
  27. data/lib/appforce/spawn.rb +312 -0
  28. data/lib/appforce/spawn/api.rb +4 -0
  29. data/lib/appforce/spawn/api/call.rb +217 -0
  30. data/lib/appforce/spawn/exceptions.rb +30 -0
  31. data/lib/appforce/spawn/runner.rb +143 -0
  32. data/lib/appforce/spawn/template.rb +102 -0
  33. data/lib/appforce/spawn/version.rb +10 -0
  34. data/spec/api_call_spec.rb +380 -0
  35. data/spec/config_spec.rb +51 -0
  36. data/spec/fixtures/all_host_data.json +12 -0
  37. data/spec/fixtures/appforce_config.yml +4 -0
  38. data/spec/fixtures/fake_private_key.yml +2 -0
  39. data/spec/fixtures/host_scout_vars.yml +10 -0
  40. data/spec/fixtures/hosts +8 -0
  41. data/spec/fixtures/inactive_users.yml +3 -0
  42. data/spec/fixtures/malformed_appforce_config.yml +4 -0
  43. data/spec/fixtures/private_key_vars.yml +4 -0
  44. data/spec/fixtures/scout_main.yml +2 -0
  45. data/spec/fixtures/users.yml +6 -0
  46. data/spec/fixtures/vars.yml +4 -0
  47. data/spec/logger_spec.rb +85 -0
  48. data/spec/runner_spec.rb +308 -0
  49. data/spec/spec_helper.rb +53 -0
  50. data/spec/template_spec.rb +160 -0
  51. data/spec/version_spec.rb +9 -0
  52. data/tmp/.keep +0 -0
  53. metadata +151 -0
@@ -0,0 +1,10 @@
1
+ module Appforce
2
+ module Spawn
3
+ MAJOR = 0
4
+ MINOR = 5
5
+ SUB = 1
6
+
7
+ # must always be \d.\d.\d format
8
+ VERSION = [MAJOR, MINOR, SUB].join('.')
9
+ end
10
+ end
@@ -0,0 +1,380 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
2
+
3
+ RSpec.describe Appforce::Spawn::Api::Call do
4
+ before do
5
+ logger = Logger.new('/dev/null')
6
+ Appforce::Spawn.logger = logger
7
+ Appforce::Config.load_config(file_fixture_path('appforce_config.yml'))
8
+ end
9
+
10
+ describe '.latest_version' do
11
+ before do
12
+ @last_modified = Date.new(2010, 1, 15).to_s
13
+ @content_length = '1024'
14
+ @request_object = HTTParty::Request.new Net::HTTP::Get, '/'
15
+ @response_object = Net::HTTPOK.new('1.1', 200, 'OK')
16
+ allow(@response_object).to receive_messages(body: '{"version":"0.1"}')
17
+ @response_object['last-modified'] = @last_modified
18
+ @response_object['content-length'] = @content_length
19
+ @parsed_response = lambda { {"version" => "0.1"} }
20
+ @response = HTTParty::Response.new(@request_object, @response_object, @parsed_response)
21
+ allow(HTTParty).to receive(:get).and_return(@response)
22
+ end
23
+
24
+ it 'should return the latest version number from RubyGems' do
25
+ info = Appforce::Spawn::Api::Call.latest_version
26
+ expect(info).to be_a String
27
+ expect(info).to eq '0.1'
28
+ end
29
+ end
30
+
31
+ describe '.get_hosts' do
32
+ context 'missing parameters (Client API Name)' do
33
+ it 'should raise an exception' do
34
+ expect { Appforce::Spawn::Api::Call.get_hosts(*[nil, {}]) }.to raise_exception(Appforce::Spawn::MissingParameters)
35
+ end
36
+ end
37
+
38
+ context 'has Client API Name parameter' do
39
+ before do
40
+ resp = double('resp')
41
+ allow(resp).to receive(:code).and_return(200)
42
+ allow(resp).to receive(:value).and_return('good')
43
+ allow(HTTParty).to receive(:get).and_return(resp)
44
+ end
45
+
46
+ it 'should return an API response' do
47
+ api_resp = Appforce::Spawn::Api::Call.get_hosts(*[nil, {:client_api_name => 'template_test'}])
48
+ expect(api_resp.value).to eq 'good'
49
+ end
50
+ end
51
+ end
52
+
53
+ describe '.get_vars' do
54
+ context 'missing parameters (Client API Name)' do
55
+ it 'should raise an exception' do
56
+ expect { Appforce::Spawn::Api::Call.get_vars(*[nil, {}]) }.to raise_exception(Appforce::Spawn::MissingParameters)
57
+ end
58
+ end
59
+
60
+ context 'has Client API Name parameter' do
61
+ before do
62
+ resp = double('resp')
63
+ allow(resp).to receive(:code).and_return(200)
64
+ allow(resp).to receive(:value).and_return('good')
65
+ allow(HTTParty).to receive(:get).and_return(resp)
66
+ end
67
+
68
+ it 'should return an API response' do
69
+ api_resp = Appforce::Spawn::Api::Call.get_vars(*[nil, {:client_api_name => 'template_test'}])
70
+ expect(api_resp.value).to eq 'good'
71
+ end
72
+ end
73
+ end
74
+
75
+ describe '.get_active_users' do
76
+ context 'missing parameters (Client API Name)' do
77
+ it 'should raise an exception' do
78
+ expect { Appforce::Spawn::Api::Call.get_active_users(*[nil, {}]) }.to raise_exception(Appforce::Spawn::MissingParameters)
79
+ end
80
+ end
81
+
82
+ context 'has Client API Name parameter' do
83
+ before do
84
+ resp = double('resp')
85
+ allow(resp).to receive(:code).and_return(200)
86
+ allow(resp).to receive(:value).and_return('good')
87
+ allow(HTTParty).to receive(:get).and_return(resp)
88
+ end
89
+
90
+ it 'should return an API response' do
91
+ api_resp = Appforce::Spawn::Api::Call.get_active_users(*[nil, {:client_api_name => 'template_test'}])
92
+ expect(api_resp.value).to eq 'good'
93
+ end
94
+ end
95
+ end
96
+
97
+ describe '.get_inactive_users' do
98
+ context 'missing parameters (Client API Name)' do
99
+ it 'should raise an exception' do
100
+ expect { Appforce::Spawn::Api::Call.get_inactive_users(*[nil, {}]) }.to raise_exception(Appforce::Spawn::MissingParameters)
101
+ end
102
+ end
103
+
104
+ context 'has Client API Name parameter' do
105
+ before do
106
+ resp = double('resp')
107
+ allow(resp).to receive(:code).and_return(200)
108
+ allow(resp).to receive(:value).and_return('good')
109
+ allow(HTTParty).to receive(:get).and_return(resp)
110
+ end
111
+
112
+ it 'should return an API response' do
113
+ api_resp = Appforce::Spawn::Api::Call.get_inactive_users(*[nil, {:client_api_name => 'template_test'}])
114
+ expect(api_resp.value).to eq 'good'
115
+ end
116
+ end
117
+ end
118
+
119
+ describe '.get_private_key' do
120
+ context 'missing parameters (Client API Name)' do
121
+ it 'should raise an exception' do
122
+ expect { Appforce::Spawn::Api::Call.get_private_key(*[nil, {}]) }.to raise_exception(Appforce::Spawn::MissingParameters)
123
+ end
124
+ end
125
+
126
+ context 'has Client API Name parameter' do
127
+ before do
128
+ resp = double('resp')
129
+ allow(resp).to receive(:code).and_return(200)
130
+ allow(resp).to receive(:value).and_return('good')
131
+ allow(HTTParty).to receive(:get).and_return(resp)
132
+ end
133
+
134
+ it 'should return an API response' do
135
+ api_resp = Appforce::Spawn::Api::Call.get_private_key(*[nil, {:client_api_name => 'template_test'}])
136
+ expect(api_resp.value).to eq 'good'
137
+ end
138
+ end
139
+ end
140
+
141
+ describe '.ping' do
142
+ context 'Host is available' do
143
+ before do
144
+ resp = double('resp')
145
+ allow(resp).to receive(:code).and_return(200)
146
+ allow(resp).to receive(:value).and_return('good')
147
+ allow(HTTParty).to receive(:get).and_return(resp)
148
+ end
149
+
150
+ it 'should return 200' do
151
+ ping = Appforce::Spawn::Api::Call.ping
152
+ expect(ping.code).to eq 200
153
+ expect(ping.value).to eq 'good'
154
+ end
155
+ end
156
+ end
157
+
158
+ describe '.get_clients' do
159
+ context 'Host is available' do
160
+ before do
161
+ resp = double('resp')
162
+ allow(resp).to receive(:code).and_return(200)
163
+ allow(resp).to receive(:value).and_return('good')
164
+ allow(HTTParty).to receive(:get).and_return(resp)
165
+ end
166
+
167
+ it 'should return 200' do
168
+ ping = Appforce::Spawn::Api::Call.get_clients
169
+ expect(ping.code).to eq 200
170
+ expect(ping.value).to eq 'good'
171
+ end
172
+ end
173
+ end
174
+
175
+ describe '.get_all_host_data' do
176
+ context 'missing parameters (Client API Name)' do
177
+ it 'should raise an exception' do
178
+ expect { Appforce::Spawn::Api::Call.get_all_host_data(*[nil, {}]) }.to raise_exception(Appforce::Spawn::MissingParameters)
179
+ end
180
+ end
181
+
182
+ context 'has Client API Name parameter' do
183
+ before do
184
+ resp = double('resp')
185
+ allow(resp).to receive(:code).and_return(200)
186
+ allow(resp).to receive(:value).and_return('good')
187
+ allow(HTTParty).to receive(:get).and_return(resp)
188
+ end
189
+
190
+ it 'should return an API response' do
191
+ api_resp = Appforce::Spawn::Api::Call.get_all_host_data(*[nil, {:client_api_name => 'template_test'}])
192
+ expect(api_resp.value).to eq 'good'
193
+ end
194
+ end
195
+ end
196
+
197
+ describe '.get_main_scout' do
198
+ context 'missing parameters (Client API Name)' do
199
+ it 'should raise an exception' do
200
+ expect { Appforce::Spawn::Api::Call.get_main_scout(*[nil, {}]) }.to raise_exception(Appforce::Spawn::MissingParameters)
201
+ end
202
+ end
203
+
204
+ context 'has Client API Name parameter' do
205
+ before do
206
+ resp = double('resp')
207
+ allow(resp).to receive(:code).and_return(200)
208
+ allow(resp).to receive(:value).and_return('good')
209
+ allow(HTTParty).to receive(:get).and_return(resp)
210
+ end
211
+
212
+ it 'should return an API response' do
213
+ api_resp = Appforce::Spawn::Api::Call.get_main_scout(*[nil, {:client_api_name => 'template_test'}])
214
+ expect(api_resp.value).to eq 'good'
215
+ end
216
+ end
217
+ end
218
+
219
+ describe '.get_host_scout_vars' do
220
+ context 'missing parameters (Host API Name)' do
221
+ it 'should raise an exception' do
222
+ expect { Appforce::Spawn::Api::Call.get_host_scout_vars(*[nil, {}]) }.to raise_exception(Appforce::Spawn::MissingParameters)
223
+ end
224
+ end
225
+
226
+ context 'has Client API Name parameter' do
227
+ before do
228
+ resp = double('resp')
229
+ allow(resp).to receive(:code).and_return(200)
230
+ allow(resp).to receive(:value).and_return('good')
231
+ allow(HTTParty).to receive(:get).and_return(resp)
232
+ end
233
+
234
+ it 'should return an API response' do
235
+ api_resp = Appforce::Spawn::Api::Call.get_host_scout_vars(*[nil, {:host_api_name => '1'}])
236
+ expect(api_resp.value).to eq 'good'
237
+ end
238
+ end
239
+ end
240
+
241
+ describe '.list_clients' do
242
+ context 'Host is available' do
243
+ before do
244
+ resp = Enumerator.new do |y|
245
+ 1.times do |x|
246
+ y << {"name"=>"test", "api_name"=>"test"}
247
+ end
248
+ end
249
+ allow(resp).to receive(:value).and_return('good')
250
+ allow(resp).to receive(:code).and_return(200)
251
+ allow(HTTParty).to receive(:get).and_return(resp)
252
+ end
253
+
254
+ it 'should return 200' do
255
+ expect { Appforce::Spawn::Api::Call.list_clients }.to output(" == Client List ==============================================================\n Client: test API Name: test\n =============================================================================\n").to_stdout
256
+ end
257
+ end
258
+ end
259
+
260
+ describe '.list_hosts' do
261
+ context 'Host is available' do
262
+ before do
263
+ allow(Appforce::Spawn::Api::Call).to receive(:get_all_host_data).and_return(file_fixture_to_json('all_host_data.json'))
264
+ end
265
+
266
+ it 'should write a list of hosts to screen + return the JSON list of hosts' do
267
+ resp = nil
268
+ expect { resp = Appforce::Spawn::Api::Call.list_hosts }.to output(" == Host List ================================================================\n 1 - 'ubuntu-test01' host: ubuntu-test01 ip: 192.168.10.3\n =============================================================================\n").to_stdout
269
+ expect(resp).to eq file_fixture_to_json('all_host_data.json')
270
+ end
271
+ end
272
+ end
273
+
274
+ describe '.ssh_to_host' do
275
+ context 'Host is available' do
276
+ before do
277
+ allow(Appforce::Spawn::Api::Call).to receive(:get_all_host_data).and_return(file_fixture_to_json('all_host_data.json'))
278
+ allow(Appforce::Spawn::Api::Call).to receive(:ssh_cmd).and_return('touch tmp/fork')
279
+ allow(HighLine).to receive(:ask).and_return(1)
280
+ end
281
+
282
+ it 'should execute the command returned by the ssh_cmd call based on the input from the user' do
283
+ expect { Appforce::Spawn::Api::Call.ssh_to_host }.to output(" == Host List ================================================================\n 1 - 'ubuntu-test01' host: ubuntu-test01 ip: 192.168.10.3\n =============================================================================\n").to_stdout
284
+ end
285
+ end
286
+ end
287
+
288
+ context 'private methods' do
289
+ describe '.ssh_cmd' do
290
+ it 'should return a valid ssh command' do
291
+ params = {'hostname' => 'test', 'username' => 'user', 'method' => 'hostname' }
292
+ expect(Appforce::Spawn::Api::Call.send(:ssh_cmd, params)).to eq 'ssh user@test'
293
+ end
294
+ end
295
+
296
+ describe '.base_api_url' do
297
+ it 'should return the API URL driven by the .appforce config' do
298
+ expect(Appforce::Spawn::Api::Call.send(:base_api_url)).to eq 'http://afuka.synctree.com/VERSION_STRING/'
299
+ end
300
+ end
301
+
302
+ describe '.client_url' do
303
+ it 'should return the API URL for the Client' do
304
+ expect(Appforce::Spawn::Api::Call.send(:client_url, 'test')).to eq 'http://afuka.synctree.com/VERSION_STRING/client/test'
305
+ end
306
+ end
307
+
308
+ describe '.token_param' do
309
+ it 'should return the token parameter string' do
310
+ expect(Appforce::Spawn::Api::Call.send(:token_param)).to eq 'token=INVALID_TOKEN'
311
+ end
312
+ end
313
+
314
+ describe '.make_api_call' do
315
+ context 'get a valid API response' do
316
+ before do
317
+ resp = double('resp')
318
+ allow(resp).to receive(:code).and_return(200)
319
+ allow(resp).to receive(:value).and_return('good')
320
+ allow(HTTParty).to receive(:get).and_return(resp)
321
+ end
322
+
323
+ it 'should return the response object' do
324
+ api_resp = Appforce::Spawn::Api::Call.send(:make_api_call, 'url')
325
+ expect(api_resp.value).to eq 'good'
326
+ end
327
+ end
328
+
329
+ context 'use an invalid token' do
330
+ before do
331
+ resp = double('resp')
332
+ allow(resp).to receive(:code).and_return(401)
333
+ allow(resp).to receive(:value).and_return('invalid')
334
+ allow(HTTParty).to receive(:get).and_return(resp)
335
+ end
336
+
337
+ it 'should raise an Appforce::Spawn::API::UnauthorizedAccess exception' do
338
+ expect { Appforce::Spawn::Api::Call.send(:make_api_call, 'url') }.to raise_exception(Appforce::Spawn::API::UnauthorizedAccess)
339
+ end
340
+ end
341
+
342
+ context 'hit an invalid route' do
343
+ before do
344
+ resp = double('resp')
345
+ allow(resp).to receive(:code).and_return(404)
346
+ allow(resp).to receive(:value).and_return('invalid')
347
+ allow(HTTParty).to receive(:get).and_return(resp)
348
+ end
349
+
350
+ it 'should raise an Appforce::Spawn::API::NotFound exception' do
351
+ expect { Appforce::Spawn::Api::Call.send(:make_api_call, 'url') }.to raise_exception(Appforce::Spawn::API::NotFound)
352
+ end
353
+ end
354
+
355
+ context 'service is unavailable' do
356
+ before do
357
+ resp = double('resp')
358
+ allow(resp).to receive(:code).and_return(502)
359
+ allow(resp).to receive(:value).and_return('invalid')
360
+ allow(HTTParty).to receive(:get).and_return(resp)
361
+ end
362
+
363
+ it 'should raise an Appforce::Spawn::API::Unavailable exception' do
364
+ expect { Appforce::Spawn::Api::Call.send(:make_api_call, 'url') }.to raise_exception(Appforce::Spawn::API::Unavailable)
365
+ end
366
+ end
367
+
368
+ context 'HTTParty raises an exception' do
369
+ before do
370
+ allow(HTTParty).to receive(:get).and_raise('BOOM')
371
+ end
372
+
373
+ it 'should raise an Appforce::Spawn::API::CallFailure exception' do
374
+ expect { Appforce::Spawn::Api::Call.send(:make_api_call, 'url') }.to raise_exception(Appforce::Spawn::API::CallFailure)
375
+ end
376
+ end
377
+ end
378
+ end
379
+
380
+ end
@@ -0,0 +1,51 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
2
+
3
+ RSpec.describe Appforce::Config do
4
+ before do
5
+ logger = Logger.new('/dev/null')
6
+ Appforce::Spawn.logger = logger
7
+ end
8
+
9
+ describe 'DEFAULT_CONFIG_PATH' do
10
+ it 'should have a default config path' do
11
+ expect(Appforce::Config::DEFAULT_CONFIG_PATH).to end_with '.appforce'
12
+ end
13
+
14
+ context '.load_config' do
15
+ it 'should load a config at a given path with method calls for keys in config' do
16
+ Appforce::Config.load_config(file_fixture_path('appforce_config.yml'))
17
+ @config = Appforce::Config.config
18
+ expect(@config.api_host).to eq 'http://afuka.synctree.com'
19
+ expect(@config.api_version).to eq 'VERSION_STRING'
20
+ expect(@config.api_token).to eq 'INVALID_TOKEN'
21
+ end
22
+
23
+ it 'should raise an excpetion with a malformed config file' do
24
+ expect { Appforce::Config.load_config(file_fixture_path('malformed_appforce_config.yml')) }.to raise_exception
25
+ end
26
+ end
27
+
28
+ context '.dump_example_config' do
29
+ before do
30
+ allow(File).to receive(:expand_path).and_return("tmp")
31
+ end
32
+
33
+ it 'should write out a sample .appforce config file' do
34
+ Appforce::Config.dump_example_config
35
+ cfg = open_tmp_file('.appforce.example')
36
+ expect(cfg).to match /PUT_API_TOKEN_HERE/
37
+ end
38
+
39
+ context 'errors while writing file' do
40
+ before do
41
+ allow(File).to receive(:open).and_raise(IOError)
42
+ end
43
+
44
+ it 'should raise an IOError excpetion' do
45
+ expect { Appforce::Config.dump_example_config }.to raise_exception
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ end
@@ -0,0 +1,12 @@
1
+ [{
2
+ "id": "1",
3
+ "hostname": "ubuntu-test01",
4
+ "ip": "192.168.10.3",
5
+ "desc": "ubuntu-test01",
6
+ "active": "true",
7
+ "rvm": "true",
8
+ "scout": "true",
9
+ "env": "staging",
10
+ "method": "hostname",
11
+ "username": "test.st"
12
+ }]