cheftacular 2.0.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.
Files changed (118) hide show
  1. checksums.yaml +7 -0
  2. data/bin/cft +4 -0
  3. data/bin/cftclr +4 -0
  4. data/bin/cheftacular +4 -0
  5. data/bin/client-list +4 -0
  6. data/lib/cheftacular/README.md +416 -0
  7. data/lib/cheftacular/actions/check.rb +32 -0
  8. data/lib/cheftacular/actions/console.rb +62 -0
  9. data/lib/cheftacular/actions/database.rb +13 -0
  10. data/lib/cheftacular/actions/db_console.rb +67 -0
  11. data/lib/cheftacular/actions/deploy.rb +40 -0
  12. data/lib/cheftacular/actions/log.rb +47 -0
  13. data/lib/cheftacular/actions/migrate.rb +57 -0
  14. data/lib/cheftacular/actions/run.rb +64 -0
  15. data/lib/cheftacular/actions/scale.rb +94 -0
  16. data/lib/cheftacular/actions/tail.rb +55 -0
  17. data/lib/cheftacular/actions.rb +14 -0
  18. data/lib/cheftacular/auditor.rb +46 -0
  19. data/lib/cheftacular/chef/data_bag.rb +104 -0
  20. data/lib/cheftacular/cheftacular.rb +55 -0
  21. data/lib/cheftacular/decryptors.rb +45 -0
  22. data/lib/cheftacular/encryptors.rb +48 -0
  23. data/lib/cheftacular/getters.rb +153 -0
  24. data/lib/cheftacular/helpers.rb +296 -0
  25. data/lib/cheftacular/initializers.rb +451 -0
  26. data/lib/cheftacular/parsers.rb +199 -0
  27. data/lib/cheftacular/remote_helpers.rb +30 -0
  28. data/lib/cheftacular/stateless_action.rb +16 -0
  29. data/lib/cheftacular/stateless_actions/add_ssh_key_to_bag.rb +44 -0
  30. data/lib/cheftacular/stateless_actions/arguments.rb +68 -0
  31. data/lib/cheftacular/stateless_actions/backups.rb +116 -0
  32. data/lib/cheftacular/stateless_actions/bootstrappers/centos_bootstrap.rb +7 -0
  33. data/lib/cheftacular/stateless_actions/bootstrappers/coreos_bootstrap.rb +7 -0
  34. data/lib/cheftacular/stateless_actions/bootstrappers/fedora_bootstrap.rb +7 -0
  35. data/lib/cheftacular/stateless_actions/bootstrappers/redhat_bootstrap.rb +7 -0
  36. data/lib/cheftacular/stateless_actions/bootstrappers/ubuntu_bootstrap.rb +102 -0
  37. data/lib/cheftacular/stateless_actions/bootstrappers/vyatta_bootstrap.rb +7 -0
  38. data/lib/cheftacular/stateless_actions/chef_bootstrap.rb +40 -0
  39. data/lib/cheftacular/stateless_actions/chef_environment.rb +21 -0
  40. data/lib/cheftacular/stateless_actions/clean_cookbooks.rb +104 -0
  41. data/lib/cheftacular/stateless_actions/clean_sensu_plugins.rb +19 -0
  42. data/lib/cheftacular/stateless_actions/clean_server_passwords.rb +14 -0
  43. data/lib/cheftacular/stateless_actions/cleanup_log_files.rb +14 -0
  44. data/lib/cheftacular/stateless_actions/client_list.rb +89 -0
  45. data/lib/cheftacular/stateless_actions/cloud.rb +107 -0
  46. data/lib/cheftacular/stateless_actions/cloud_bootstrap.rb +109 -0
  47. data/lib/cheftacular/stateless_actions/compile_audit_log.rb +60 -0
  48. data/lib/cheftacular/stateless_actions/compile_readme.rb +41 -0
  49. data/lib/cheftacular/stateless_actions/create_git_key.rb +67 -0
  50. data/lib/cheftacular/stateless_actions/disk_report.rb +75 -0
  51. data/lib/cheftacular/stateless_actions/environment.rb +100 -0
  52. data/lib/cheftacular/stateless_actions/fetch_file.rb +24 -0
  53. data/lib/cheftacular/stateless_actions/fix_known_hosts.rb +70 -0
  54. data/lib/cheftacular/stateless_actions/full_bootstrap.rb +30 -0
  55. data/lib/cheftacular/stateless_actions/get_active_ssh_connections.rb +18 -0
  56. data/lib/cheftacular/stateless_actions/get_haproxy_log.rb +55 -0
  57. data/lib/cheftacular/stateless_actions/get_log_from_bag.rb +38 -0
  58. data/lib/cheftacular/stateless_actions/get_pg_pass.rb +61 -0
  59. data/lib/cheftacular/stateless_actions/help.rb +71 -0
  60. data/lib/cheftacular/stateless_actions/initialize_data_bag_contents.rb +220 -0
  61. data/lib/cheftacular/stateless_actions/knife_upload.rb +23 -0
  62. data/lib/cheftacular/stateless_actions/pass.rb +49 -0
  63. data/lib/cheftacular/stateless_actions/reinitialize.rb +46 -0
  64. data/lib/cheftacular/stateless_actions/remove_client.rb +81 -0
  65. data/lib/cheftacular/stateless_actions/replication_status.rb +103 -0
  66. data/lib/cheftacular/stateless_actions/restart_swap.rb +55 -0
  67. data/lib/cheftacular/stateless_actions/rvm.rb +14 -0
  68. data/lib/cheftacular/stateless_actions/server_update.rb +99 -0
  69. data/lib/cheftacular/stateless_actions/service.rb +14 -0
  70. data/lib/cheftacular/stateless_actions/test_env.rb +82 -0
  71. data/lib/cheftacular/stateless_actions/update_split_branches.rb +64 -0
  72. data/lib/cheftacular/stateless_actions/update_tld.rb +62 -0
  73. data/lib/cheftacular/stateless_actions/upload_nodes.rb +120 -0
  74. data/lib/cheftacular/stateless_actions/upload_roles.rb +24 -0
  75. data/lib/cheftacular/version.rb +5 -0
  76. data/lib/cheftacular.rb +4 -0
  77. data/lib/cloud_interactor/authentication.rb +56 -0
  78. data/lib/cloud_interactor/cloud_interactor.rb +23 -0
  79. data/lib/cloud_interactor/domain/create.rb +17 -0
  80. data/lib/cloud_interactor/domain/create_record.rb +27 -0
  81. data/lib/cloud_interactor/domain/destroy.rb +17 -0
  82. data/lib/cloud_interactor/domain/destroy_record.rb +23 -0
  83. data/lib/cloud_interactor/domain/list.rb +9 -0
  84. data/lib/cloud_interactor/domain/list_records.rb +22 -0
  85. data/lib/cloud_interactor/domain/read.rb +23 -0
  86. data/lib/cloud_interactor/domain/read_record.rb +27 -0
  87. data/lib/cloud_interactor/domain/update.rb +18 -0
  88. data/lib/cloud_interactor/domain/update_record.rb +42 -0
  89. data/lib/cloud_interactor/domain.rb +18 -0
  90. data/lib/cloud_interactor/flavor.rb +27 -0
  91. data/lib/cloud_interactor/helpers.rb +70 -0
  92. data/lib/cloud_interactor/image.rb +27 -0
  93. data/lib/cloud_interactor/parser.rb +37 -0
  94. data/lib/cloud_interactor/server/attach_volume.rb +33 -0
  95. data/lib/cloud_interactor/server/create.rb +39 -0
  96. data/lib/cloud_interactor/server/destroy.rb +11 -0
  97. data/lib/cloud_interactor/server/detach_volume.rb +21 -0
  98. data/lib/cloud_interactor/server/list.rb +7 -0
  99. data/lib/cloud_interactor/server/list_volumes.rb +25 -0
  100. data/lib/cloud_interactor/server/poll.rb +22 -0
  101. data/lib/cloud_interactor/server/read.rb +9 -0
  102. data/lib/cloud_interactor/server/read_volume.rb +24 -0
  103. data/lib/cloud_interactor/server.rb +17 -0
  104. data/lib/cloud_interactor/version.rb +4 -0
  105. data/lib/cloud_interactor/volume/create.rb +13 -0
  106. data/lib/cloud_interactor/volume/destroy.rb +11 -0
  107. data/lib/cloud_interactor/volume/list.rb +7 -0
  108. data/lib/cloud_interactor/volume/read.rb +9 -0
  109. data/lib/cloud_interactor/volume.rb +20 -0
  110. data/lib/ridley/monkeypatches.rb +11 -0
  111. data/lib/sshkit/actions/start_commit_check.rb +19 -0
  112. data/lib/sshkit/actions/start_deploy.rb +25 -0
  113. data/lib/sshkit/actions/start_log_fetch.rb +91 -0
  114. data/lib/sshkit/actions/start_task.rb +29 -0
  115. data/lib/sshkit/getters.rb +67 -0
  116. data/lib/sshkit/helpers.rb +13 -0
  117. data/lib/sshkit/monkeypatches.rb +19 -0
  118. metadata +375 -0
@@ -0,0 +1,451 @@
1
+
2
+ class Cheftacular
3
+ class Initializer
4
+ def initialize options, config
5
+ @options, @config = options, config
6
+
7
+ initialize_yaml_configuration
8
+
9
+ initialize_environment
10
+
11
+ initialize_locations
12
+
13
+ initialize_monkeypatches unless @config['helper'].running_on_chef_node?
14
+
15
+ initialize_arguments
16
+
17
+ initialize_sub_environment
18
+
19
+ initialize_cloud_options
20
+
21
+ initialize_documentation_hash
22
+
23
+ @config['helper'].completion_rate? 0, 'initializer'
24
+
25
+ initialize_ridley
26
+
27
+ @config['helper'].completion_rate? 10, 'initializer'
28
+
29
+ initialize_ridley_environments
30
+
31
+ @config['helper'].completion_rate? 20, 'initializer'
32
+
33
+ initialize_ridley_roles_and_nodes
34
+
35
+ @config['helper'].completion_rate? 30, 'initializer'
36
+
37
+ initialize_data_bags_for_environment @options['env'], true
38
+
39
+ @config['helper'].completion_rate? 90, 'initializer'
40
+
41
+ initialize_ruby_config
42
+
43
+ initialize_passwords @options['env']
44
+
45
+ initialize_classes
46
+
47
+ initialize_directories
48
+
49
+ @config['helper'].completion_rate? 100, 'initializer'
50
+
51
+ initialize_version_check if @config['cheftacular']['strict_version_checks'] == 'true'
52
+
53
+ initialize_auditing_checks if @config['cheftacular']['auditing'] == 'true'
54
+ end
55
+
56
+ #changes to arguments should show up in the documentation methods in their appropriate method file
57
+ def initialize_arguments
58
+ OptionParser.new do |opts|
59
+ opts.banner = "Usage: cft command [repository] [opts]"
60
+
61
+ # Environment arguments
62
+
63
+ opts.on('-b', '--datastaging', 'Set the environment to datastaging') do
64
+ @options['env'] = 'datastaging'
65
+ end
66
+
67
+ opts.on('-d', '--dev-remote', "Set the environment to devremote") do
68
+ @options['env'] = 'devremote'
69
+ end
70
+
71
+ opts.on('--env ENV', 'Set the environment to one you specify') do |env|
72
+ @options['env'] = env
73
+ end
74
+
75
+ opts.on('-p', '--prod', "Set the environment to production") do
76
+ @options['env'] = 'production'
77
+ end
78
+
79
+ opts.on('-Q','--qa', 'Set the environment to QA') do
80
+ @options['env'] = 'qa'
81
+ end
82
+
83
+ opts.on('-s', '--staging', "Set the environment to staging (this is the default)") do
84
+ @options['env'] = 'staging'
85
+ end
86
+
87
+ opts.on('--split-env SPLIT_ENV_NAME', "Set the sub-environment to the specified split_env") do |sub_env|
88
+ @options['sub_env'] = sub_env
89
+ end
90
+
91
+ opts.on('-t', '--test', 'Set the environment to test') do
92
+ @options['env'] = 'test'
93
+ end
94
+
95
+ # General arguments
96
+
97
+ opts.on('-a', '--address ADDRESS', "Run your command against this address") do |addr|
98
+ @options['address'] = addr
99
+ end
100
+
101
+ opts.on('-D', '--debug', "Activate extremely verbose logging") do
102
+ @options['debug'] = true
103
+ @options['verbose'] = true
104
+ end
105
+
106
+ opts.on('-n', '--node-name NAME', "Run your command against this node_name") do |name|
107
+ @options['node_name'] = name
108
+ end
109
+
110
+ opts.on('-q', '--quiet', "Deactivates most forms of output") do
111
+ @options['quiet'] = true
112
+ end
113
+
114
+ opts.on('-r', '--role-name NAME', "Run your command against this role_name") do |name|
115
+ @options['role'] = name
116
+ end
117
+
118
+ opts.on('-R', '--repository NAME', 'Run your command against this repository / context') do |name|
119
+ @options['repository'] = name
120
+ end
121
+
122
+ opts.on('-v', '--verbose', "Activates slightly more verbose logging, also causes commands to output to terminal and logs") do
123
+ @options['verbose'] = true
124
+ end
125
+
126
+ opts.on('--no-logs', "Do not make logs for any command") do
127
+ @options['no_logs'] = true
128
+ end
129
+
130
+ opts.on('-h', '--help', 'Displays the README') do
131
+ @config['helper'].display_readme
132
+
133
+ puts "Remember, you can also utilize the cft help command!"
134
+
135
+ exit
136
+ end
137
+
138
+ # Action Arguments
139
+
140
+ opts.on('-e', '--except-role NAME', 'For deployments, will prevent the deploy from triggering on servers with this role') do |name|
141
+ @options['negative_role'] = name
142
+ end
143
+
144
+ opts.on('-z', '--unset-revision', 'Tells the chef-server that we want to return to using the default revision for a repository') do
145
+ @options['unset_revision'] = true
146
+ end
147
+
148
+ opts.on('-Z REVISION', '--revision REVISION', "Tells the chef-server what branch / revision it should deploy for a repository") do |revision|
149
+ @options['target_revision'] = revision
150
+ end
151
+
152
+ # client-list
153
+ opts.on('-W', '--with-priv', "On client-list this will show each server's private addresses") do
154
+ @options['with_private'] = true
155
+ end
156
+
157
+ # cft log options
158
+ opts.on('--nginx', "On cft log pass this argument to fetch nginx logs as well as application logs") do
159
+ @options['get_nginx_logs'] = true
160
+ end
161
+
162
+ opts.on('--full', "On cft log pass this argument to fetch the FULL log") do
163
+ @options['get_full_logs'] = true
164
+ end
165
+
166
+ opts.on('-l INTEGER', '--lines INTEGER', "On cft log pass this argument to fetch the last X lines of logs") do |num|
167
+ @options['get_log_lines'] = num
168
+ end
169
+
170
+ opts.on('--num INTEGER', "On cft log pass this argument to fetch the last X lines of logs") do |num|
171
+ @options['get_log_lines'] = num
172
+ end
173
+
174
+ #cft run
175
+ opts.on('--all', "On cft run COMMAND you can pass --all to run the command on multiple nodes") do
176
+ @options['run_on_all'] = true
177
+ end
178
+
179
+ #cloud_bootstrap
180
+
181
+ opts.on('--with-dn DOMAIN_NAME', "On hip rax_bootstrap allows you to specify a domain structure other than the default environment one") do |domain|
182
+ @options['with_dn'] = domain
183
+ end
184
+
185
+ #cloud
186
+
187
+ opts.on('-o', '--cloud CLOUD_NAME', "On cft cloud calls, set the cloud to the one you specify") do |cloud_name|
188
+ @options['preferred_cloud'] = cloud_name
189
+ end
190
+
191
+ opts.on('--rax', "On cft cloud calls, set the cloud to Rackspace") do
192
+ @options['preferred_cloud'] = 'rackspace'
193
+ end
194
+
195
+ opts.on('--aws', "On cft cloud calls, set the cloud to Amazon Web Services") do
196
+ @options['preferred_cloud'] = 'aws'
197
+ end
198
+
199
+ opts.on('--region REGION', 'On cft cloud calls, set the cloud region to perform operations on to this region') do |region|
200
+ @options['preferred_cloud_region'] = region
201
+ end
202
+
203
+ opts.on('--image IMAGE', 'On cft cloud calls, set the default image to this image (can be shorthand like "Ubuntu 14.04"') do |image|
204
+ @options['preferred_cloud_image'] = image
205
+ end
206
+
207
+ opts.on('--virtualization-mode MODE', 'On cft cloud calls, set the default virtualization mode to this (On rackspace, only PV or PVHVM are supported)') do |v_mode|
208
+ @options['virtualization_mode'] = v_mode
209
+ end
210
+
211
+ end.parse!
212
+ end
213
+
214
+ def initialize_yaml_configuration
215
+ config_location = if File.exist?(File.join( Dir.getwd, 'config', 'cheftacular.yml' ))
216
+ File.join( Dir.getwd, 'config', 'cheftacular.yml' )
217
+ elsif File.exist?('/root/cheftacular.yml')
218
+ '/root/cheftacular.yml'
219
+ else
220
+ raise "cheftacular.yml configuration file could not be found in either #{ File.join( Dir.getwd, 'config', 'cheftacular.yml' ) } or /root/cheftacular.yml"
221
+ end
222
+
223
+ @config['cheftacular'] = YAML::load(ERB.new(IO.read(File.open(config_location))).result)
224
+ rescue StandardError => e
225
+ puts "The cheftacular.yml configuration file could not be parsed."
226
+ puts "Error message: #{ e }\n#{ e.backtrace.join("\n") }"
227
+
228
+ exit
229
+ end
230
+
231
+ def initialize_environment
232
+ @options['env'] = @config['cheftacular']['initial_environment'] if @config['cheftacular'].has_key?('initial_environment')
233
+ end
234
+
235
+ def initialize_monkeypatches
236
+ if File.exists?(File.expand_path("#{ @config['locs']['app-root'] }/config/initializers/cheftacular.rb"))
237
+ puts "Cheftacular Monkeypatch file detected! Preparing to require..."
238
+
239
+ require "#{ @config['locs']['app-root'] }/config/initializers/cheftacular"
240
+ end
241
+ end
242
+
243
+ def initialize_cloud_options
244
+ @config['helper'].set_cloud_options
245
+ end
246
+
247
+ #only matters to the config_bag and it's hash. Used to fetch keys within the bag for certain commands
248
+ def initialize_sub_environment
249
+ @options['sub_env'] ||= @options['env']
250
+ end
251
+
252
+ def initialize_documentation_hash
253
+ @config['documentation'] ||= {}
254
+ @config['documentation']['arguments'] ||= []
255
+ @config['documentation']['action'] ||= []
256
+ @config['documentation']['stateless_action'] ||= []
257
+ @config['documentation']['application'] ||= []
258
+ @config['documentation']['devops'] ||= []
259
+ end
260
+
261
+ def initialize_locations
262
+ locs ||= {}
263
+
264
+ if @config['helper'].running_in_mode? 'application'
265
+ locs['root'] = Dir.getwd
266
+ locs['chef-log'] = File.join( locs['root'], 'log')
267
+ locs['app-root'] = locs['root']
268
+ elsif @config['helper'].running_on_chef_node?
269
+ locs['chef-log'] = File.expand_path("/root/sensu_log")
270
+ locs['chef'] = File.expand_path("/etc/chef")
271
+ locs['ssh'] = File.expand_path('/home/deploy/.ssh')
272
+ end
273
+
274
+ locs['chef-repo'] = Dir.getwd
275
+ locs['roles'] = File.expand_path("#{ locs['chef-repo'] }/roles")
276
+ locs['nodes'] = File.expand_path("#{ locs['chef-repo'] }/nodes_dir") #DO NOT RENAME THIS TO NODES
277
+ locs['root'] = locs['chef-repo'] unless locs['root']
278
+ locs['app-root'] = locs['chef-repo'] unless locs['app-root']
279
+ locs['chef'] = File.expand_path("~/.chef") unless locs['chef']
280
+ locs['cookbooks'] = File.expand_path("#{ locs['chef-repo'] }/cookbooks")
281
+ locs['berks'] = File.expand_path('~/.berkshelf/cookbooks')
282
+ locs['wrapper-cookbooks'] = @config['cheftacular']['wrapper-cookbooks']
283
+ locs['ssh'] = File.expand_path('~/.ssh')
284
+ locs['chef-log'] = File.expand_path("#{ locs['root']}/log") unless locs['chef-log']
285
+
286
+ @config['locs'] = locs
287
+ end
288
+
289
+ def initialize_ridley
290
+ return unless @config['ridley'].nil?
291
+
292
+ @config['data_bag_secret'] = File.read(File.expand_path("#{ @config['locs']['chef'] }/#{ @config['cheftacular']['data_bag_key_file'] }")).chomp
293
+
294
+ Ridley::Logging.logger.level = Logger.const_get 'ERROR'
295
+
296
+ @config['ridley'] = Ridley.new(
297
+ server_url: @config['cheftacular']['chef_server_url'],
298
+ client_name: (@config['helper'].running_on_chef_node? ? parse_node_name_from_client_file : @config['cheftacular']['cheftacular_chef_user']),
299
+ client_key: File.expand_path("#{ @config['locs']['chef'] }/#{ @config['helper'].running_on_chef_node? ? 'client.pem' : @config['cheftacular']['cheftacular_chef_user'] }.pem"),
300
+ encrypted_data_bag_secret: @config['data_bag_secret'],
301
+ ssl: { verify: @config['cheftacular']['ssl_verify'] == 'true' }
302
+ )
303
+ end
304
+
305
+ def initialize_ridley_environments
306
+ @config['chef_environments'] ||= @config['ridley'].environment.all.map { |env| env.name }.delete_if { |env| env == '_default' }
307
+ end
308
+
309
+ def initialize_ridley_roles_and_nodes
310
+ @config['chef_nodes'] ||= @config['ridley'].node.all
311
+ @config['chef_roles'] ||= @config['ridley'].role.all
312
+ end
313
+
314
+ def initialize_data_bags_for_environment env, in_initializer=false, bags_to_load=[]
315
+ @config['ChefDataBag'] ||= Cheftacular::ChefDataBag.new(@options, @config)
316
+
317
+ puts("Loading additional data bag data from chef server for environment \"#{ env }\" for bags: #{ bags_to_load.join(', ') }") if !in_initializer && !@options['quiet']
318
+
319
+ @config['ChefDataBag'].init_bag('default', 'authentication', false) if bags_to_load.empty? || bags_to_load.include?('authentication')
320
+
321
+ @config['helper'].completion_rate?(38, 'initializer') if in_initializer
322
+
323
+ @config['ChefDataBag'].init_bag(env, 'addresses', false) if bags_to_load.empty? || bags_to_load.include?('addresses')
324
+
325
+ @config['helper'].completion_rate?(46, 'initializer') if in_initializer
326
+
327
+ @config['ChefDataBag'].init_bag(env, 'audit', false) if bags_to_load.empty? || bags_to_load.include?('audit')
328
+
329
+ @config['helper'].completion_rate?(54, 'initializer') if in_initializer
330
+
331
+ @config['ChefDataBag'].init_bag(env, 'chef_passwords') if bags_to_load.empty? || bags_to_load.include?('chef_passwords')
332
+
333
+ @config['helper'].completion_rate?(62, 'initializer') if in_initializer
334
+
335
+ @config['ChefDataBag'].init_bag(env, 'config', false) if bags_to_load.empty? || bags_to_load.include?('config')
336
+
337
+ @config['helper'].completion_rate?(70, 'initializer') if in_initializer
338
+
339
+ @config['ChefDataBag'].init_bag(env, 'logs', false) if bags_to_load.empty? || bags_to_load.include?('logs')
340
+
341
+ @config['helper'].completion_rate?(78, 'initializer') if in_initializer
342
+
343
+ @config['ChefDataBag'].init_bag(env, 'node_roles', false) if bags_to_load.empty? || bags_to_load.include?('node_roles')
344
+
345
+ @config['helper'].completion_rate?(86, 'initializer') if in_initializer
346
+
347
+ @config['ChefDataBag'].init_bag(env, 'server_passwords') if bags_to_load.empty? || bags_to_load.include?('server_passwords')
348
+ end
349
+
350
+ def initialize_ruby_config
351
+ @config['ruby_string'] = @config['cheftacular']['ruby_version']
352
+
353
+ begin
354
+ @config['ruby_string'] = File.read(File.expand_path("#{ locs['app-root'] }/.ruby-version")) unless @config['ruby_string']
355
+ rescue StandardError => e
356
+ msg = [
357
+ "Please run this in the root of your application directory,",
358
+ " a ruby string to run commands against was not found in either your cheftacular.yml file or your .ruby-version file."
359
+ ]
360
+
361
+ @config['helper'].exception_output msg, e
362
+ end
363
+
364
+ @config['ruby_string'] = "ruby-" + @config['ruby_string'] unless @config['ruby_string'].include?('ruby-')
365
+
366
+ #TODO Reevaluate for non-rvm setups
367
+ @config['bundle_command'] = "/home/#{ @config['cheftacular']['deploy_user'] }/.rvm/gems/#{ @config['ruby_string'].chomp }@global/bin/bundle"
368
+ end
369
+
370
+ def initialize_passwords env, refresh_bag=false
371
+ @config['server_passwords'] ||= {}
372
+
373
+ @config[env]['server_passwords_bag'].reload if refresh_bag
374
+
375
+ @config[env]['server_passwords_bag_hash'] = @config[env]['server_passwords_bag'].decrypt.to_hash if refresh_bag
376
+
377
+ #data_hash will be { server_name: 'SERVER_NAME', password: 'PASSWORD_STRING' }
378
+ @config[env]['server_passwords_bag_hash'].each_pair do |key, data_hash|
379
+ if key.include?('-deploy-pass')
380
+ addr = key.split('-deploy-pass').first
381
+
382
+ @config['server_passwords'][addr] = data_hash
383
+ end
384
+ end
385
+ end
386
+
387
+ def initialize_version_check detected_version=""
388
+ current_version = Cheftacular::VERSION
389
+
390
+ detected_version = File.exists?( @config['helper'].current_version_file_path ) ? File.read( @config['helper'].current_version_file_path ) : @config['helper'].fetch_remote_version
391
+
392
+ if @config['helper'].is_higher_version? detected_version, current_version
393
+ puts "\n Your Cheftacular is out of date. Currently #{ current_version } and remote version is #{ detected_version }.\n"
394
+
395
+ puts "Please update the gemfile to #{ detected_version } and restart this process.\n"
396
+
397
+ exit
398
+ else
399
+ unless File.exists?( current_version_file_path )
400
+ puts "Creating file cache for #{ Time.now.strftime("%Y%m%d") } (#{ detected_version }). No new version detected."
401
+
402
+ @config['helper'].write_version_file detected_version
403
+ end
404
+ end
405
+ end
406
+
407
+ def initialize_auditing_checks
408
+ unless File.exists? @config['helper'].current_audit_file_path
409
+ puts "Creating file cache for #{ Time.now.strftime("%Y%m%d") } audit data..."
410
+
411
+ @config['auditor'].write_audit_cache_file
412
+ end
413
+ end
414
+
415
+ def initialize_classes
416
+ @config['auditor'] = Cheftacular::Auditor.new(@options, @config)
417
+ @config['parser'] = Cheftacular::Parser.new(@options, @config)
418
+ @config['getter'] = Cheftacular::Getter.new(@options, @config)
419
+ @config['action'] = Cheftacular::Action.new(@options, @config)
420
+ @config['stateless_action'] = Cheftacular::StatelessAction.new(@options, @config)
421
+ @config['encryptor'] = Cheftacular::Encryptor.new(@config['data_bag_secret'])
422
+ @config['decryptor'] = Cheftacular::Decryptor.new(@config['data_bag_secret'])
423
+ @config['action_documentation'] = Cheftacular::ActionDocumentation.new(@options, @config)
424
+ @config['stateless_action_documentation'] = Cheftacular::StatelessActionDocumentation.new(@options, @config)
425
+ @config['dummy_sshkit'] = SSHKit::Backend::Netssh.new(SSHKit::Host.new('127.0.0.1'))
426
+ end
427
+
428
+ def initialize_directories
429
+ FileUtils.mkdir_p @config['locs']['chef-log']
430
+
431
+ FileUtils.mkdir_p File.join( @config['locs']['app-root'], 'tmp', @config['helper'].declassify)
432
+
433
+ FileUtils.mkdir_p @config['helper'].current_nodes_file_cache_path
434
+
435
+ @config['helper'].cleanup_file_caches
436
+ end
437
+
438
+ def initialize_bag_for_all_environments bag_name, total_percent=100
439
+ total_bags = @config['chef_environments'].count
440
+ current_bags = 1
441
+
442
+ @config['chef_environments'].each do |env|
443
+ @config['ChefDataBag'].init_bag env, bag_name
444
+
445
+ @config['helper'].completion_rate? (percent + (( (current_bags).to_f / total_bags.to_f ) * 100) / ( 100.to_f / total_percent.to_f ) ), __method__
446
+
447
+ current_bags += 1
448
+ end
449
+ end
450
+ end
451
+ end
@@ -0,0 +1,199 @@
1
+
2
+ class Cheftacular
3
+ class Parser
4
+ def initialize options, config
5
+ @options, @config = options, config
6
+ end
7
+
8
+ #parses and *validates* the inputs from the initializer
9
+ def parse_context
10
+ return if @config['repository'] && @config['command'] && @config['role']
11
+
12
+ roles ||= []
13
+
14
+ @config['chef_roles'].each {|r| roles << r.name }
15
+
16
+ @options['command'] = ARGV[0] unless @options['command']
17
+
18
+ unless @config['helper'].is_stateless_command?(@options['command'])
19
+ parse_repository(@options['repository'])
20
+
21
+ parse_role(@options['role'])
22
+ end
23
+
24
+ parse_node_name(@options['node_name']) if @options['node_name']
25
+
26
+ parse_address(@options['address']) if @options['address']
27
+
28
+ parse_and_set_revision if @options['target_revision'] || @options['unset_revision']
29
+ end
30
+
31
+ #try and get the most accurate name of the repo
32
+ def parse_application_context
33
+ working_dir = Dir.getwd.split('/').last
34
+
35
+ #if there is no mapping setup for the directory, try and parse it from the .ruby-gemset file
36
+ if File.exist?(File.expand_path("#{ @config['locs']['app-root'] }/.ruby-gemset")) && !@config['getter'].get_repository_from_role_name(working_dir, "has_value?")
37
+ working_dir = File.read(File.expand_path("#{ @config['locs']['app-root'] }/.ruby-gemset")).chomp
38
+ end
39
+
40
+ if @config['getter'].get_repository_from_role_name(working_dir, "has_value?")
41
+ parse_repository(working_dir)
42
+
43
+ @options['repository'] = working_dir
44
+
45
+ @options['command'] = ARGV[0] unless @config['helper'].is_not_command_or_stateless_command?(ARGV[0])
46
+ end
47
+
48
+ return if !@options['codebase'].nil? && !@options['role'].nil? && !@options['command'].nil?
49
+ return if !@options['command'].nil? && @config['helper'].is_stateless_command?(ARGV[0])
50
+ end
51
+
52
+ def parse_repository repository, set_variables=true
53
+ repo_check_array = []
54
+
55
+ @config['cheftacular']['repositories'].each_value do |h|
56
+ repo_check_array << h['repo_name'].include?(repository) unless repository.nil?
57
+ end
58
+
59
+ if repository.nil? && @config['helper'].running_in_mode?('devops')
60
+ raise "Unable to parse a repository, please pass in the argument -c REPOSITORY to pass a repo"
61
+
62
+ elsif repo_check_array.include?(true)
63
+ @config['cheftacular']['repositories'].each_pair do |key, repo_hash|
64
+ @options['role'] = key if repo_hash['repo_name'] == repository && set_variables
65
+ end
66
+ else
67
+ raise "Unable to parse repository: #{ repository }, the repository you're referring to does not exist in your cheftacular.yml."
68
+ end
69
+ end
70
+
71
+ def parse_role role, mode="set"
72
+ roles ||= []
73
+ @config['chef_roles'].each {|r| roles << r.name }
74
+
75
+ case mode
76
+ when 'set'
77
+ if role.nil? then raise "Unable to parse a role, please pass in the argument -r ROLE_NAME to pass a role"
78
+ elsif roles.include?(role) then @options['role'] = role
79
+ else raise "Unable to parse role: #{ role }, #{ role } does not represent a valid role"
80
+ end
81
+ when 'boolean'
82
+ roles.include?(role)
83
+ end
84
+ end
85
+
86
+ def parse_node_name name
87
+ nodes ||= []
88
+ @config['chef_nodes'].each {|n| nodes << n.name }
89
+
90
+ if name.nil? then raise "You attempted to specify a node_name but did not pass one, please use -n NODE_NAME"
91
+ elsif nodes.include?(name) then @options['node_name'] = name
92
+ else raise "Unable to parse node_name: #{ name }, the node you're referring to does not exist."
93
+ end
94
+ end
95
+
96
+ def parse_address address
97
+ addresses ||= []
98
+ @config['chef_nodes'].each {|n| addresses << n.public_ipaddress }
99
+
100
+ if address.nil? then raise "You attempted to specify an address but did not pass one, please use -a IP_ADDRESS"
101
+ elsif addresses.include?(address) then @options['address'] = address
102
+ else raise "Unable to parse address: #{ address }, the address you're referring to is not part of any environment"
103
+ end
104
+ end
105
+
106
+ def parse_and_set_revision
107
+ raise "Cannot set or unset target_revision without a role" unless @options['role']
108
+
109
+ if @options['target_revision']
110
+ @config[@options['env']]['config_bag_hash'][@options['sub_env']]['app_revisions'][@config['getter'].get_codebase_from_role_name(@options['role'])] = @options['target_revision']
111
+
112
+ elsif @options['unset_revision']
113
+ @config[@options['env']]['config_bag_hash'][@options['sub_env']]['app_revisions'][@config['getter'].get_codebase_from_role_name(@options['role'])] = "<use_default>"
114
+
115
+ end
116
+
117
+ @config['ChefDataBag'].save_config_bag
118
+ end
119
+
120
+ def array_of_nodes_contains_node_name? nodes, node_name, names=[]
121
+ nodes.each { |node| names << node['name'] }
122
+
123
+ names.include? node_name
124
+ end
125
+
126
+ def index_of_node_name_in_array_of_nodes nodes, node_name, names=[]
127
+ nodes.each { |node| names << node['name'] }
128
+
129
+ names.index node_name
130
+ end
131
+
132
+ #parse nodes out of array based on hash, ex: [{ unless: 'role[rails]'}, {if: 'role[worker]'}, { if: { run_list: 'role[db]', role: 'pg_data' } }]
133
+ def exclude_nodes nodes, statement_arr, only_one_node = false, ret_arr = []
134
+ nodes.each do |n|
135
+ go_next = false
136
+
137
+ statement_arr.each do |statement_hash|
138
+ statement_hash.each_pair do |if_key, statement|
139
+ if statement.is_a?(String)
140
+ self.instance_eval("go_next = true #{ if_key.to_s } n.run_list.include?('#{ statement }')")
141
+
142
+ elsif statement.is_a?(Hash)
143
+ eval_string = "go_next = true #{ if_key.to_s } "
144
+ eval_list = []
145
+
146
+ statement.each_pair do |run_key, check_val|
147
+ eval_list << "n.run_list.include?('#{ check_val }')" if run_key == :run_list
148
+ eval_list << "!n.run_list.include?('#{ check_val }')" if run_key == :not_run_list
149
+ eval_list << "n.chef_environment == '#{ check_val }'" if run_key == :env
150
+ eval_list << "n.chef_environment != '#{ check_val }'" if run_key == :not_env
151
+ eval_list << "@options['role'] == '#{ check_val }'" if run_key == :role
152
+ eval_list << "@options['role'] != '#{ check_val }'" if run_key == :not_role
153
+ eval_list << "n.name == '#{ check_val }'" if run_key == :node
154
+ eval_list << "n.name != '#{ check_val }'" if run_key == :not_node
155
+ eval_list << "#{ check_val }" if run_key == :eval #careful with this, you need to pass in an already parsed string
156
+ end
157
+
158
+ self.instance_eval(eval_string + eval_list.join(' && '))
159
+ else
160
+ raise "Invalid statement type (#{ statement.class }) - Statement must be string or hash"
161
+ end
162
+ end
163
+ end
164
+
165
+ next if go_next
166
+
167
+ ret_arr << n
168
+
169
+ break if only_one_node
170
+ end
171
+
172
+ if @options['verbose'] && @options['command'] != "client_list"
173
+ puts("Parsed #{ ret_arr.count } nodes. Preparing to run on #{ ret_arr.map { |n| n.name }.join(',') } in env #{ @options['env'] } on role #{ @options['role'] }")
174
+ end
175
+
176
+ ret_arr
177
+ end
178
+
179
+ def parse_runtime_arguments num_of_args=0, mode='normal'
180
+ case mode
181
+ when 'normal'
182
+ case num_of_args
183
+ when 0 then raise "You attempted to run #{ __method__ } with 0 args! Look up this method from the stacktrace!"
184
+ when 1 then ARGV[num_of_args-1]
185
+ when 2..100 then ARGV[0..(num_of_args-1)]
186
+ end
187
+ when 'range' then ARGV[1..ARGV.length-1].join(' ')
188
+ else raise "You passed #{ mode }. This is not yet implemented for #{ __method__ }"
189
+ end
190
+ end
191
+
192
+ def parse_to_dns dns_string
193
+ raise "Unable to parse DNS without @option['node_name'] for #{ dns_string }!" if dns_string.include?('NODE_NAME') && @options['node_name'].nil?
194
+ raise "Unable to parse DNS without a tld set in the config bag for #{ @options['env'] }!" if dns_string.include?('ENV_TLD') && @config[@options['env']]['config_bag_hash']['tld'].nil?
195
+
196
+ dns_string.gsub('NODE_NAME', @options['node_name']).gsub('ENV_TLD', @config[@options['env']]['config_bag_hash']['tld'])
197
+ end
198
+ end
199
+ end
@@ -0,0 +1,30 @@
1
+
2
+ class Cheftacular
3
+ module RemoteHelpers
4
+ def set_log_loc_and_timestamp
5
+ @dummy_sshkit.set_log_loc_and_timestamp( @locs )
6
+ end
7
+
8
+ def start_tail_log ip_address, run_list
9
+ true_env = @config['dummy_sshkit'].get_true_environment run_list, @config['cheftacular']['run_list_environments'], @options['env']
10
+
11
+ #special servers should be listed first as most of them will have web role
12
+ log_loc = case
13
+ when run_list.include?('role[sensu_server]')
14
+ "/var/log/sensu/sensu-server.log"
15
+ when run_list.include?('role[graphite_server]')
16
+ "/var/log/carbon-cache/current"
17
+ when run_list.include?('role[web]') && !run_list.include?('nodejs')
18
+ "/var/www/vhosts/#{ get_codebase_from_role_name( @options['role']) }/current/log/#{ true_env }.log"
19
+ when run_list.include?('role[worker]') || run_list.include?('nodejs')
20
+ "/var/log/syslog"
21
+ else
22
+ puts "This gem is not currently configured to handle tailing this case"
23
+ return 0
24
+ end
25
+
26
+
27
+ `ssh -oStrictHostKeyChecking=no -tt deploy@#{ ip_address } "#{ sudo(ip_address) } tail -f #{ log_loc }" > /dev/tty`
28
+ end
29
+ end
30
+ end