orats 0.5.1 → 0.6.0

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.
@@ -1,107 +0,0 @@
1
- require 'orats/version'
2
- require 'orats/shell'
3
- require 'orats/foreman'
4
-
5
- module Orats
6
- class Command
7
- include Thor::Base
8
- include Thor::Shell
9
- include Thor::Actions
10
- #source_root Dir.pwd
11
-
12
- include Shell
13
- include Foreman
14
-
15
- attr_accessor :active_path
16
-
17
- def initialize(app_name = '', options = {})
18
- @app_name = app_name
19
- @options = options
20
-
21
- # required to mix in thor actions without having a base thor class
22
- #@destination_stack = [self.class.source_root]
23
- self.destination_root = Dir.pwd
24
- @behavior = :invoke
25
- end
26
-
27
- def new
28
- @active_path = @app_name
29
- @active_path = services_path(@app_name)
30
-
31
- rails_template 'base' do
32
- gsub_postgres_info
33
- git_commit 'Change the postgres information'
34
-
35
- unless @options[:redis_password].empty?
36
- gsub_redis_info
37
- git_commit 'Add the redis password'
38
- end
39
-
40
- gsub_project_path
41
- git_commit 'Add the development project path'
42
-
43
- bundle_install
44
- git_commit 'Add gem lock file'
45
-
46
- bundle_binstubs
47
- git_commit 'Add binstubs for the important gems'
48
-
49
- spring_binstub
50
- git_commit 'Springify all of the bins'
51
-
52
- run_rake 'db:create:all db:migrate'
53
- git_commit 'Add the database schema file'
54
- end
55
-
56
- if @options[:auth]
57
- rails_template 'auth', '--skip ' do
58
- run_rake 'db:migrate db:seed'
59
- end
60
- end
61
-
62
- unless @options[:skip_extras]
63
- ansible_init @app_name
64
- end
65
-
66
- @active_path = services_path(@app_name)
67
- foreman_init
68
- end
69
-
70
- def play
71
- play_app @app_name
72
- end
73
-
74
- def nuke
75
- @active_path = @app_name
76
-
77
- nuke_warning
78
-
79
- nuke_data_details_warning unless @options[:skip_data]
80
-
81
- confirmed_to_delete = yes?('Are you sure? (y/N)', :cyan)
82
-
83
- if confirmed_to_delete
84
- nuke_data unless @options[:skip_data]
85
-
86
- nuke_directory
87
- end
88
- end
89
-
90
- def outdated
91
- outdated_init
92
- end
93
-
94
- def version
95
- puts "Orats version #{VERSION}"
96
- end
97
-
98
- private
99
- def active_project
100
- File.basename @active_path
101
- end
102
-
103
- def services_path(app_name)
104
- @options[:skip_extras] ? app_name : "#{app_name}/services/#{active_project}"
105
- end
106
- end
107
- end
@@ -1,54 +0,0 @@
1
- require 'socket'
2
- require 'timeout'
3
-
4
- module Orats
5
- module Foreman
6
- def foreman_init
7
-
8
- @options[:skip_foreman_start] ? message = 'Start your' : message = 'Starting'
9
-
10
- puts '', '='*80
11
- say_status 'action', "\e[1m#{message} server with the following commands:\e[0m", :cyan
12
- say_status 'command', "cd #{@active_path}", :magenta
13
- say_status 'command', 'bundle exec foreman start', :magenta
14
- puts '='*80, ''
15
-
16
- attempt_to_start unless @options[:skip_foreman_start]
17
- end
18
-
19
- private
20
-
21
- def attempt_to_start
22
- while port_taken? do
23
- puts
24
- say_status 'error', "\e[1mAnother application is using port 3000\n\e[0m", :red
25
- puts '-'*80
26
-
27
- exit 1 if no?('Would you like to try running the server again? (y/N)', :cyan)
28
- end
29
-
30
- puts
31
-
32
- run_from @active_path, 'bundle exec foreman start'
33
- end
34
-
35
- def port_taken?
36
- begin
37
- Timeout::timeout(5) do
38
- begin
39
- s = TCPSocket.new('localhost', 3000)
40
- s.close
41
-
42
- return true
43
- rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
44
- return false
45
- end
46
- end
47
- rescue Timeout::Error
48
- false
49
- end
50
-
51
- false
52
- end
53
- end
54
- end
@@ -1,503 +0,0 @@
1
- require 'securerandom'
2
- require 'open-uri'
3
-
4
- module Orats
5
- module Shell
6
- def run_from(path, command)
7
- run "cd #{path} && #{command} && cd -"
8
- end
9
-
10
- def log_message(type, message)
11
- puts
12
- say_status type, "#{message}...", :yellow
13
- puts '-'*80, ''; sleep 0.25
14
- end
15
-
16
- def log_status(type, message, color)
17
- puts
18
- say_status type, set_color(message, :bold), color
19
- end
20
-
21
- def log_status_under(type, message, color)
22
- say_status type, message, color
23
- puts
24
- end
25
-
26
- def log_results(results, message)
27
- log_status 'results', results, :magenta
28
- log_status_under 'message', message, :white
29
- end
30
-
31
- def git_commit(message)
32
- run_from @active_path, "git add -A && git commit -m '#{message}'"
33
- end
34
-
35
- def gsub_postgres_info
36
- log_message 'root', 'Changing the postgres information'
37
-
38
- gsub_file "#{@active_path}/.env", 'DATABASE_HOST: localhost', "DATABASE_HOST: #{@options[:pg_location]}"
39
- gsub_file "#{@active_path}/.env", ': postgres', ": #{@options[:pg_username]}"
40
- gsub_file "#{@active_path}/.env", ': supersecrets', ": #{@options[:pg_password]}"
41
- end
42
-
43
- def gsub_redis_info
44
- log_message 'root', 'Adding the redis password'
45
-
46
- gsub_file "#{@active_path}/config/initializers/sidekiq.rb", '//', "//:#{ENV['CACHE_PASSWORD']}@"
47
- gsub_file "#{@active_path}/.env", 'HE_PASSWORD: ', "HE_PASSWORD: #{@options[:redis_password]}"
48
- gsub_file "#{@active_path}/.env", 'CACHE_HOST: localhost', "CACHE_HOST: #{@options[:redis_location]}"
49
- gsub_file "#{@active_path}/config/application.rb", '# pass', 'pass'
50
- end
51
-
52
- def gsub_project_path
53
- log_message 'root', 'Changing the project path'
54
-
55
- gsub_file "#{@active_path}/.env", ': /full/path/to/your/project', ": #{File.expand_path(@active_path)}"
56
- end
57
-
58
- def run_rake(command)
59
- log_message 'shell', 'Running rake commands'
60
-
61
- run_from @active_path, "bundle exec rake #{command}"
62
- end
63
-
64
- def bundle_install
65
- log_message 'shell', 'Running bundle install, this may take a while'
66
-
67
- run_from @active_path, 'bundle install'
68
- end
69
-
70
- def bundle_binstubs
71
- log_message 'shell', 'Running bundle binstubs for a few gems'
72
-
73
- run_from @active_path, 'bundle binstubs whenever puma sidekiq'
74
- end
75
-
76
- def spring_binstub
77
- log_message 'shell', 'Running spring binstub'
78
-
79
- run_from @active_path, 'bundle exec spring binstub --all'
80
- end
81
-
82
- def nuke_warning
83
- puts
84
- say_status 'nuke', "\e[1mYou are about to permanently delete this directory:\e[0m", :red
85
- say_status 'path', "#{File.expand_path(@app_name)}", :yellow
86
- puts
87
- end
88
-
89
- def rails_directories
90
- rails_gemfiles = run("find #{@active_path} -type f -name Gemfile | xargs grep -lE \"gem 'rails'|gem \\\"rails\\\"\"", capture: true)
91
- gemfile_paths = rails_gemfiles.split("\n")
92
-
93
- gemfile_paths.map { |gemfile| File.dirname(gemfile) }
94
- end
95
-
96
- def nuke_data_details_warning
97
- rails_projects = []
98
-
99
- rails_directories.each do |rails_dir|
100
- rails_projects << File.basename(rails_dir)
101
- end
102
-
103
- project_names = rails_projects.join(', ')
104
-
105
- puts
106
- say_status 'nuke', "\e[1mYou are about to permanently delete all postgres databases for:\e[0m", :red
107
- say_status 'databases', project_names, :yellow
108
- puts
109
- say_status 'nuke', "\e[1mYou are about to permanently delete all redis namespaces for:\e[0m", :red
110
- say_status 'namespace', project_names, :yellow
111
- puts
112
- end
113
-
114
- def nuke_data
115
- rails_directories.each do |directory|
116
- log_message 'root', 'Removing postgres databases'
117
- run_from directory, 'bundle exec rake db:drop:all'
118
- nuke_redis File.basename(directory)
119
- end
120
- end
121
-
122
- def can_play?
123
- log_message 'shell', 'Checking for the ansible binary'
124
-
125
- has_ansible = run('which ansible', capture: true)
126
-
127
- dependency_error 'Cannot access ansible',
128
- 'Are you sure you have ansible setup correctly?',
129
- 'http://docs.ansible.com/intro_installation.html`' if has_ansible.empty?
130
-
131
- !has_ansible.empty?
132
- end
133
-
134
- def rails_template(command, flags = '')
135
- exit_if_cannot_rails
136
- exit_if_exists unless flags.index(/--skip/)
137
-
138
- run "rails new #{@active_path} #{flags} --skip-bundle --template #{File.expand_path File.dirname(__FILE__)}/templates/#{command}.rb"
139
- yield if block_given?
140
- end
141
-
142
- def play_app(path)
143
- return unless can_play?
144
-
145
- @active_path = path
146
- rails_template 'play'
147
- end
148
-
149
- def ansible_init(path)
150
- log_message 'shell', 'Creating ansible inventory'
151
- run "mkdir #{path}/inventory"
152
- run "mkdir #{path}/inventory/group_vars"
153
- copy_from_includes 'inventory/hosts', path
154
- copy_from_includes 'inventory/group_vars/all.yml', path
155
-
156
- secrets_path = "#{path}/secrets"
157
- log_message 'shell', 'Creating ansible secrets'
158
- run "mkdir #{secrets_path}"
159
-
160
- save_secret_string "#{secrets_path}/postgres_password"
161
-
162
- if @options[:redis_password].empty?
163
- run "touch #{secrets_path}/redis_password"
164
- else
165
- save_secret_string "#{secrets_path}/redis_password"
166
- gsub_file "#{path}/inventory/group_vars/all.yml", 'redis_password: false', 'redis_password: true'
167
- end
168
-
169
- save_secret_string "#{secrets_path}/mail_password"
170
- save_secret_string "#{secrets_path}/rails_token"
171
- save_secret_string "#{secrets_path}/devise_token"
172
- save_secret_string "#{secrets_path}/devise_pepper_token"
173
-
174
- log_message 'shell', 'Modifying secrets path in group_vars/all.yml'
175
- gsub_file "#{path}/inventory/group_vars/all.yml", '~/tmp/testproj/secrets/', File.expand_path(secrets_path)
176
-
177
- log_message 'shell', 'Modifying the place holder app name in group_vars/all.yml'
178
- gsub_file "#{path}/inventory/group_vars/all.yml", 'testproj', File.basename(path)
179
- gsub_file "#{path}/inventory/group_vars/all.yml", 'TESTPROJ', File.basename(path).upcase
180
-
181
- log_message 'shell', 'Creating ssh keypair'
182
- run "ssh-keygen -t rsa -P '' -f #{secrets_path}/id_rsa"
183
-
184
- log_message 'shell', 'Creating self signed ssl certificates'
185
- run create_rsa_certificate(secrets_path, 'sslkey.key', 'sslcert.crt')
186
-
187
- log_message 'shell', 'Creating monit pem file'
188
- run "#{create_rsa_certificate(secrets_path, 'monit.pem', 'monit.pem')} && openssl gendh 512 >> #{secrets_path}/monit.pem"
189
-
190
- install_role_dependencies unless @options[:skip_galaxy]
191
- end
192
-
193
- def outdated_init
194
- latest_gem_version = compare_gem_version
195
-
196
- github_repo = "https://raw.githubusercontent.com/nickjj/orats/#{latest_gem_version}/lib/orats"
197
-
198
- galaxy_url = "#{github_repo}/templates/includes/Galaxyfile"
199
- playbook_url = "#{github_repo}/templates/play.rb"
200
- inventory_url = "#{github_repo}/templates/includes/inventory/group_vars/all.yml"
201
-
202
- remote_galaxy_contents = url_to_string(galaxy_url)
203
- remote_playbook_contents = url_to_string(playbook_url)
204
- remote_inventory_contents = url_to_string(inventory_url)
205
-
206
- compare_remote_role_version_to_local remote_galaxy_contents
207
-
208
- local_playbook = compare_remote_to_local('playbook',
209
- 'roles',
210
- playbook_file_stats(remote_playbook_contents),
211
- playbook_file_stats(IO.read(playbook_file_path)))
212
-
213
- local_inventory = compare_remote_to_local('inventory',
214
- 'variables',
215
- inventory_file_stats(remote_inventory_contents),
216
- inventory_file_stats(IO.read(inventory_file_path)))
217
-
218
- unless @options[:playbook].empty?
219
- compare_user_to_local('playbook', 'roles', @options[:playbook], local_playbook) do
220
- playbook_file_stats IO.read(@options[:playbook])
221
- end
222
- end
223
-
224
- unless @options[:inventory].empty?
225
- compare_user_to_local('inventory', 'variables', @options[:inventory], local_inventory) do
226
- inventory_file_stats IO.read(@options[:inventory])
227
- end
228
- end
229
- end
230
-
231
- private
232
-
233
- def inventory_file_stats(file)
234
- # pluck out all of the values contained with {{ }}
235
- ansible_variable_list = file.scan(/\{\{([^{{}}]*)\}\}/)
236
-
237
- # remove the leading space
238
- ansible_variable_list.map! { |line| line.first[0] = '' }
239
-
240
- # match every line that is not a comment and contains a colon
241
- inventory_variable_list = file.scan(/^[^#].*:/)
242
-
243
- inventory_variable_list.map! do |line|
244
- # only strip lines that need it
245
- line.strip! if line.include?(' ') || line.include?("\n")
246
-
247
- # get rid of the trailing colon
248
- line.chomp(':')
249
-
250
- # if a value of a certain variable has a colon then the regex
251
- # picks this up as a match. only take the variable name
252
- # if this happens to occur
253
- line.split(':').first if line.include?(':')
254
- end
255
-
256
- (ansible_variable_list + inventory_variable_list).uniq
257
- end
258
-
259
- def playbook_file_stats(file)
260
- # match every line that is not a comment and has a role defined
261
- roles_list = file.scan(/^.*role:.*/)
262
-
263
- roles_list.map! do |line|
264
- # only strip lines that need it
265
- line.strip! if line.include?(' ') || line.include?("\n")
266
-
267
- role_parts = line.split('role:')
268
-
269
- line = role_parts[1]
270
-
271
- if line.include?(',')
272
- line = line.split(',').first
273
- end
274
-
275
- line.strip! if line.include?(' ')
276
- end
277
-
278
- roles_list.reject! { |line| line.start_with?('#') }
279
-
280
- roles_list.uniq
281
- end
282
-
283
- def compare_gem_version
284
- latest_gem_contents = `gem list orats --remote`.split.last
285
-
286
- if latest_gem_contents.include?('ERROR')
287
- say_status 'error', "\e[1mError running `gem list orats --remote`:\e[0m", :red
288
- say_status 'msg', 'Chances are their API is down, try again soon', :yellow
289
- exit 1
290
- end
291
-
292
- latest_gem_version = "v#{latest_gem_contents.split.first[1...-1]}"
293
-
294
- log_status 'gem', 'Comparing this version of orats to the latest orats version:', :green
295
- log_status_under 'version', "Latest: #{latest_gem_version}, Yours: v#{VERSION}", :yellow
296
-
297
- latest_gem_version
298
- end
299
-
300
- def compare_remote_role_version_to_local(remote_galaxy_contents)
301
- remote_galaxy_list = remote_galaxy_contents.split
302
- local_galaxy_contents = IO.read(galaxy_file_path)
303
- local_galaxy_list = local_galaxy_contents.split
304
-
305
- galaxy_difference = remote_galaxy_list - local_galaxy_list
306
-
307
- local_role_count = local_galaxy_list.size
308
- different_roles = galaxy_difference.size
309
-
310
- log_status 'roles', "Comparing this version of orats' roles to the latest version:", :green
311
-
312
- if different_roles == 0
313
- log_status_under 'message', "All #{local_role_count} roles are up to date", :yellow
314
- else
315
- log_status_under 'message', "There are #{different_roles} differences", :yellow
316
-
317
- galaxy_difference.each do |role_line|
318
- name = role_line.split(',').first
319
- status = 'outdated'
320
- color = :yellow
321
-
322
- unless local_galaxy_contents.include?(name)
323
- status = 'missing'
324
- color = :red
325
- end
326
-
327
- say_status status, name, color
328
- end
329
-
330
- log_results 'The latest version of orats may benefit you:', 'Check github to see if the changes interest you'
331
- end
332
- end
333
-
334
- def compare_remote_to_local(label, keyword, remote_list, local_list)
335
- list_difference = remote_list - local_list
336
-
337
- remote_count = list_difference.size#log_unmatched remote_list, local_list, 'remote', :yellow
338
-
339
- log_status label, "Comparing this version of orats' #{label} to the latest version:", :green
340
- log_status_under 'file', label == 'playbook' ? 'site.yml' : 'all.yml', :yellow
341
-
342
- list_difference.each do |line|
343
- say_status 'missing', line, :red unless local_list.include?(line)
344
- end
345
-
346
- if remote_count > 0
347
- log_results "#{remote_count} new #{keyword} are available:", 'You may benefit from upgrading to the latest orats'
348
- else
349
- log_results 'Everything appears to be in order:', "No missing #{keyword} were found"
350
- end
351
-
352
- local_list
353
- end
354
-
355
- def compare_user_to_local(label, keyword, user_path, local_list)
356
- if File.exist?(user_path) && File.file?(user_path)
357
- user_list = yield
358
-
359
- just_file_name = user_path.split('/').last
360
-
361
- log_status label, "Comparing this version of orats' #{label} to #{just_file_name}:", :blue
362
- log_status_under 'path', user_path, :cyan
363
-
364
- missing_count = log_unmatched local_list, user_list, 'missing', :red
365
- extra_count = log_unmatched user_list, local_list, 'extra', :yellow
366
-
367
- if missing_count > 0
368
- log_results "#{missing_count} #{keyword} are missing:", "Your ansible run will likely fail with this #{label}"
369
- else
370
- log_results 'Everything appears to be in order:', "No missing #{keyword} were found"
371
- end
372
-
373
- if extra_count > 0
374
- log_results "#{extra_count} extra #{keyword} were detected:", "No problem but remember to add them to a future #{keyword}"
375
- else
376
- log_results "No extra #{keyword} were found:", "Extra #{keyword} are fine but you have none"
377
- end
378
- else
379
- log_status label, "Comparing this version of orats' #{label} to ???:", :blue
380
- puts
381
- say_status 'error', "\e[1mError comparing #{label}:\e[0m", :red
382
- say_status 'path', user_path, :yellow
383
- say_status 'help', 'Make sure you supply a file name', :white
384
- end
385
- end
386
-
387
- def url_to_string(url)
388
- begin
389
- file_contents = open(url).read
390
- rescue OpenURI::HTTPError => ex
391
- say_status 'error', "\e[1mError browsing #{url}:\e[0m", :red
392
- say_status 'msg', ex, :yellow
393
- exit 1
394
- end
395
-
396
- file_contents
397
- end
398
-
399
- def log_unmatched(compare, against, label, color)
400
- count = 0
401
-
402
- compare.each do |item|
403
- unless against.include?(item)
404
- say_status label, item, color
405
- count += 1
406
- end
407
- end
408
-
409
- count
410
- end
411
-
412
- def create_rsa_certificate(secrets_path, keyout, out)
413
- "openssl req -new -newkey rsa:2048 -days 365 -nodes -x509 -subj '/C=US/ST=Foo/L=Bar/O=Baz/CN=qux.com' -keyout #{secrets_path}/#{keyout} -out #{secrets_path}/#{out}"
414
- end
415
-
416
- def galaxy_file_path
417
- "#{File.expand_path File.dirname(__FILE__)}/templates/includes/Galaxyfile"
418
- end
419
-
420
- def inventory_file_path
421
- "#{File.expand_path File.dirname(__FILE__)}/templates/includes/inventory/group_vars/all.yml"
422
- end
423
-
424
- def playbook_file_path
425
- "#{File.expand_path File.dirname(__FILE__)}/templates/play.rb"
426
- end
427
-
428
- def install_role_dependencies
429
- log_message 'shell', 'Updating ansible roles from the galaxy'
430
-
431
- galaxy_install = "ansible-galaxy install -r #{galaxy_file_path} --force"
432
- galaxy_out = run(galaxy_install, capture: true)
433
-
434
- if galaxy_out.include?('you do not have permission')
435
- if @options[:sudo_password].empty?
436
- sudo_galaxy_command = 'sudo'
437
- else
438
- sudo_galaxy_command = "echo #{@options[:sudo_password]} | sudo -S"
439
- end
440
-
441
- run("#{sudo_galaxy_command} #{galaxy_install}")
442
- end
443
- end
444
-
445
- def save_secret_string(file)
446
- File.open(file, 'w+') { |f| f.write(SecureRandom.hex(64)) }
447
- end
448
-
449
- def copy_from_includes(file, destination_root_path)
450
- base_path = "#{File.expand_path File.dirname(__FILE__)}/templates/includes"
451
-
452
- log_message 'shell', "Creating #{file}"
453
- run "cp #{base_path}/#{file} #{destination_root_path}/#{file}"
454
- end
455
-
456
- def nuke_redis(namespace)
457
- log_message 'root', 'Removing redis keys'
458
-
459
- run "redis-cli KEYS '#{namespace}:*' | xargs --delim='\n' redis-cli DEL"
460
- end
461
-
462
- def nuke_directory
463
- log_message 'root', 'Deleting directory'
464
-
465
- run "rm -rf #{@active_path}"
466
- end
467
-
468
- def dependency_error(message, question, answer)
469
- puts
470
- say_status 'error', "\e[1m#{message}\e[0m", :red
471
- say_status 'question', question, :yellow
472
- say_status 'answer', answer, :cyan
473
- puts '-'*80
474
- puts
475
- end
476
-
477
- def exit_if_cannot_rails
478
- log_message 'shell', 'Checking for rails'
479
-
480
- has_rails = run('which rails', capture: true)
481
-
482
- dependency_error 'Cannot access rails',
483
- 'Are you sure you have rails setup correctly?',
484
- 'You can install it by running `gem install rails`' if has_rails.empty?
485
-
486
- exit 1 if has_rails.empty?
487
- end
488
-
489
- def exit_if_exists
490
- log_message 'shell', 'Checking if a file or directory already exists'
491
-
492
- if Dir.exist?(@active_path) || File.exist?(@active_path)
493
- puts
494
- say_status 'aborting', "\e[1mA file or directory already exists at this location:\e[0m", :red
495
- say_status 'location', @active_path, :yellow
496
- puts '-'*80
497
- puts
498
-
499
- exit 1
500
- end
501
- end
502
- end
503
- end