appforce-spawn 0.5.1

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.
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
+ }]