cheftacular 2.8.0 → 2.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/lib/cheftacular/README.md +92 -70
  3. data/lib/cheftacular/actions/check.rb +3 -2
  4. data/lib/cheftacular/actions/console.rb +8 -2
  5. data/lib/cheftacular/actions/db_console.rb +28 -4
  6. data/lib/cheftacular/actions/deploy.rb +6 -8
  7. data/lib/cheftacular/actions/log.rb +7 -2
  8. data/lib/cheftacular/actions/migrate.rb +16 -5
  9. data/lib/cheftacular/actions/run.rb +10 -3
  10. data/lib/cheftacular/actions/scale.rb +4 -1
  11. data/lib/cheftacular/actions/tail.rb +8 -3
  12. data/lib/cheftacular/cheftacular.rb +1 -1
  13. data/lib/cheftacular/cloud_provider.rb +5 -1
  14. data/lib/cheftacular/dns.rb +1 -1
  15. data/lib/cheftacular/error.rb +2 -2
  16. data/lib/cheftacular/file_system.rb +50 -0
  17. data/lib/cheftacular/getter.rb +1 -1
  18. data/lib/cheftacular/helper.rb +34 -5
  19. data/lib/cheftacular/initializer.rb +15 -25
  20. data/lib/cheftacular/parser.rb +2 -0
  21. data/lib/cheftacular/stateless_actions/add_ssh_key_to_bag.rb +4 -1
  22. data/lib/cheftacular/stateless_actions/arguments.rb +8 -3
  23. data/lib/cheftacular/stateless_actions/backups.rb +5 -2
  24. data/lib/cheftacular/stateless_actions/bootstrappers/ubuntu_bootstrap.rb +8 -5
  25. data/lib/cheftacular/stateless_actions/check_cheftacular_yml_keys.rb +15 -1
  26. data/lib/cheftacular/stateless_actions/chef_bootstrap.rb +9 -2
  27. data/lib/cheftacular/stateless_actions/chef_server.rb +5 -2
  28. data/lib/cheftacular/stateless_actions/cheftacular_config.rb +26 -4
  29. data/lib/cheftacular/stateless_actions/cheftacular_yml_help.rb +31 -0
  30. data/lib/cheftacular/stateless_actions/clean_cookbooks.rb +6 -37
  31. data/lib/cheftacular/stateless_actions/clear_caches.rb +9 -2
  32. data/lib/cheftacular/stateless_actions/client_list.rb +5 -2
  33. data/lib/cheftacular/stateless_actions/cloud.rb +4 -1
  34. data/lib/cheftacular/stateless_actions/cloud_bootstrap.rb +4 -1
  35. data/lib/cheftacular/stateless_actions/compile_audit_log.rb +4 -1
  36. data/lib/cheftacular/stateless_actions/compile_readme.rb +10 -1
  37. data/lib/cheftacular/stateless_actions/create_git_key.rb +4 -1
  38. data/lib/cheftacular/stateless_actions/disk_report.rb +5 -2
  39. data/lib/cheftacular/stateless_actions/environment.rb +5 -2
  40. data/lib/cheftacular/stateless_actions/file.rb +5 -2
  41. data/lib/cheftacular/stateless_actions/fix_known_hosts.rb +5 -2
  42. data/lib/cheftacular/stateless_actions/full_bootstrap.rb +10 -2
  43. data/lib/cheftacular/stateless_actions/get_active_ssh_connections.rb +3 -2
  44. data/lib/cheftacular/stateless_actions/get_haproxy_log.rb +5 -2
  45. data/lib/cheftacular/stateless_actions/get_log_from_bag.rb +5 -2
  46. data/lib/cheftacular/stateless_actions/get_pg_pass.rb +5 -2
  47. data/lib/cheftacular/stateless_actions/get_shorewall_allowed_connections.rb +2 -1
  48. data/lib/cheftacular/stateless_actions/help.rb +28 -16
  49. data/lib/cheftacular/stateless_actions/initialize_cheftacular_yml.rb +4 -1
  50. data/lib/cheftacular/stateless_actions/initialize_data_bag_contents.rb +4 -1
  51. data/lib/cheftacular/stateless_actions/knife_upload.rb +4 -1
  52. data/lib/cheftacular/stateless_actions/list_toggleable_roles.rb +5 -2
  53. data/lib/cheftacular/stateless_actions/location_aliases.rb +5 -2
  54. data/lib/cheftacular/stateless_actions/pass.rb +5 -2
  55. data/lib/cheftacular/stateless_actions/reinitialize.rb +4 -1
  56. data/lib/cheftacular/stateless_actions/remove_client.rb +9 -2
  57. data/lib/cheftacular/stateless_actions/replication_status.rb +4 -1
  58. data/lib/cheftacular/stateless_actions/reset_bag.rb +4 -1
  59. data/lib/cheftacular/stateless_actions/restart_swap.rb +4 -1
  60. data/lib/cheftacular/stateless_actions/role_toggle.rb +7 -4
  61. data/lib/cheftacular/stateless_actions/rvm.rb +4 -1
  62. data/lib/cheftacular/stateless_actions/server_update.rb +5 -2
  63. data/lib/cheftacular/stateless_actions/service.rb +4 -1
  64. data/lib/cheftacular/stateless_actions/slack.rb +4 -1
  65. data/lib/cheftacular/stateless_actions/test_env.rb +4 -1
  66. data/lib/cheftacular/stateless_actions/update_chef_client.rb +2 -1
  67. data/lib/cheftacular/stateless_actions/update_cloudflare_dns_from_cloud.rb +4 -1
  68. data/lib/cheftacular/stateless_actions/update_split_branches.rb +4 -1
  69. data/lib/cheftacular/stateless_actions/update_thecheftacularcookbook.rb +38 -0
  70. data/lib/cheftacular/stateless_actions/update_tld.rb +4 -1
  71. data/lib/cheftacular/stateless_actions/upload_nodes.rb +6 -3
  72. data/lib/cheftacular/stateless_actions/upload_roles.rb +4 -1
  73. data/lib/cheftacular/version.rb +1 -1
  74. data/lib/cloud_interactor/authentication.rb +3 -4
  75. data/lib/cloud_interactor/flavor.rb +7 -2
  76. data/lib/cloud_interactor/helpers.rb +5 -1
  77. data/lib/cloud_interactor/server/create.rb +7 -2
  78. data/lib/sshkit/actions/start_task.rb +9 -1
  79. metadata +6 -7
  80. data/bin/client-list +0 -4
  81. data/lib/cheftacular/stateless_actions/clean_sensu_plugins.rb +0 -19
@@ -1,10 +1,17 @@
1
1
  class Cheftacular
2
2
  class ActionDocumentation
3
3
  def migrate
4
- @config['documentation']['action'] << [
4
+ @config['documentation']['action'][__method__] ||= {}
5
+ @config['documentation']['action'][__method__]['long_description'] = [
5
6
  "`cft migrate` this command will grab the first alphabetical node for a repository " +
6
- "and run a migration that will hit the database primary server."
7
+ "and run a migration that will hit the database primary server.",
8
+
9
+ [
10
+ " 1. Currently only supports rails stacks."
11
+ ]
7
12
  ]
13
+
14
+ @config['documentation']['action'][__method__]['short_description'] = 'Creates a database migration on the current environment'
8
15
  end
9
16
  end
10
17
 
@@ -29,13 +36,15 @@ class Cheftacular
29
36
 
30
37
  puts("Beginning migration run for #{ n.name } (#{ n.public_ipaddress }) on role #{ options['role'] }") unless options['quiet']
31
38
 
32
- log_data, timestamp = start_task( n.name, n.public_ipaddress, n.run_list, "#{ bundle_command } exec rake db:migrate", options, locs, cheftacular)
39
+ log_data, timestamp, exit_status = start_task( n.name, n.public_ipaddress, n.run_list, "#{ bundle_command } exec rake db:migrate", options, locs, cheftacular)
33
40
 
34
- logs_bag_hash["#{ n.name }-migrate"] = { text: log_data.scrub_pretty_text, timestamp: timestamp }
41
+ logs_bag_hash["#{ n.name }-#{ __method__ }"] = { "text" => log_data.scrub_pretty_text, "timestamp" => timestamp, "exit_status" => exit_status }
35
42
  end
36
43
 
37
44
  @config['ChefDataBag'].save_logs_bag
38
45
 
46
+ @config['helper'].send_log_bag_hash_slack_notification(logs_bag_hash, __method__, 'Failing migration detected, please fix this and deploy again, exiting...')
47
+
39
48
  @options['run_migration_already'] = true
40
49
 
41
50
  #restart the servers again after a deploy with a migration just in case
@@ -49,7 +58,9 @@ class Cheftacular
49
58
  end
50
59
 
51
60
  def migrate_nodejs nodes=[]
52
- raise "Not yet implemented"
61
+ puts "Method #{ __method__ } is not yet implemented"
62
+
63
+ exit
53
64
  end
54
65
 
55
66
  def migrate_all nodes=[]
@@ -1,7 +1,8 @@
1
1
  class Cheftacular
2
2
  class ActionDocumentation
3
3
  def run
4
- @config['documentation']['action'] << [
4
+ @config['documentation']['action'][__method__] ||= {}
5
+ @config['documentation']['action'][__method__]['long_description'] = [
5
6
  "`cft run COMMAND [--all]` will trigger the command on the first server in the role. " +
6
7
  "Can be used to run rake commands or anything else.",
7
8
 
@@ -18,6 +19,8 @@ class Cheftacular
18
19
  'arguments in quotes like `cft run "rake -T"`'
19
20
  ]
20
21
  ]
22
+
23
+ @config['documentation']['action'][__method__]['short_description'] = 'Runs a command on the current environment for the current repository'
21
24
  end
22
25
  end
23
26
 
@@ -33,6 +36,8 @@ class Cheftacular
33
36
  #must have rails stack to run migrations and not be a db, only want ONE node
34
37
  nodes = @config['parser'].exclude_nodes( nodes, [{ unless: "role[#{ @options['role'] }]" }], !@options['run_on_all'] )
35
38
 
39
+ nodes = @config['parser'].exclude_nodes( nodes, [{ if: "role[#{ @options['negative_role'] }]" }]) if @options['negative_role']
40
+
36
41
  #this must always precede on () calls so they have the instance variables they need
37
42
  options, locs, ridley, logs_bag_hash, pass_bag_hash, bundle_command, cheftacular, passwords = @config['helper'].set_local_instance_vars
38
43
 
@@ -41,12 +46,14 @@ class Cheftacular
41
46
 
42
47
  puts("Beginning task run for #{ n.name } (#{ n.public_ipaddress }) on role #{ options['role'] }") unless options['quiet']
43
48
 
44
- log_data, timestamp = start_task( n.name, n.public_ipaddress, n.run_list, "#{ bundle_command } exec #{ command }", options, locs, cheftacular)
49
+ log_data, timestamp, exit_status = start_task( n.name, n.public_ipaddress, n.run_list, "#{ bundle_command } exec #{ command }", options, locs, cheftacular)
45
50
 
46
- logs_bag_hash["#{ n.name }-run"] = { text: log_data.scrub_pretty_text, timestamp: timestamp }
51
+ logs_bag_hash["#{ n.name }-#{ __method__ }"] = { "text" => log_data.scrub_pretty_text, "timestamp" => timestamp, "exit_status" => exit_status }
47
52
  end
48
53
 
49
54
  @config['ChefDataBag'].save_logs_bag
55
+
56
+ @config['helper'].send_log_bag_hash_slack_notification(logs_bag_hash, __method__, 'Failing command detected, exiting...')
50
57
  end
51
58
 
52
59
  def run_nodejs
@@ -3,7 +3,8 @@
3
3
  class Cheftacular
4
4
  class ActionDocumentation
5
5
  def scale
6
- @config['documentation']['action'] << [
6
+ @config['documentation']['action'][__method__] ||= {}
7
+ @config['documentation']['action'][__method__]['long_description'] = [
7
8
  "`cft scale up|down [NUM_TO_SCALE]` will add (or remove) NUM_TO_SCALE servers from the server array. " +
8
9
  "This command will not let you scale down below 1 server.",
9
10
 
@@ -13,6 +14,8 @@ class Cheftacular
13
14
  "A failed build may require the server to be destroyed / examined by a DevOps engineer."
14
15
  ]
15
16
  ]
17
+
18
+ @config['documentation']['action'][__method__]['short_description'] = 'Scales an environment up or down (relies on roles)'
16
19
  end
17
20
  end
18
21
 
@@ -1,7 +1,8 @@
1
1
  class Cheftacular
2
2
  class ActionDocumentation
3
3
  def tail
4
- @config['documentation']['action'] << [
4
+ @config['documentation']['action'][__method__] ||= {}
5
+ @config['documentation']['action'][__method__]['long_description'] = [
5
6
  "`cft tail [PATTERN_TO_MATCH]` will tail the logs (return continuous output) of the first node if finds " +
6
7
  "that has an application matching the repository running on it. Currently only supports rails stacks",
7
8
 
@@ -15,6 +16,8 @@ class Cheftacular
15
16
  " 3. if the `PATTERN_TO_MATCH` argument exists, the tail will only return entries that have that pattern rather than everything written to the file."
16
17
  ]
17
18
  ]
19
+
20
+ @config['documentation']['action'][__method__]['short_description'] = 'Tails the logs of the first node found for the current repository'
18
21
  end
19
22
  end
20
23
 
@@ -26,6 +29,8 @@ class Cheftacular
26
29
 
27
30
  nodes = @config['parser'].exclude_nodes( nodes, [{ unless: "role[#{ @options['role'] }]" }], true )
28
31
 
32
+ nodes = @config['parser'].exclude_nodes( nodes, [{ if: "role[#{ @options['negative_role'] }]" }], true) if @options['negative_role']
33
+
29
34
  nodes.each do |n|
30
35
  puts("Beginning tail run for #{ n.name } (#{ n.public_ipaddress }) on role #{ @options['role'] }") unless @options['quiet']
31
36
 
@@ -50,12 +55,12 @@ class Cheftacular
50
55
 
51
56
  #special servers should be listed first as most of them will have web role
52
57
  log_loc = "#{ @config['cheftacular']['base_file_path'] }/#{ @options['repository'] }/current/log/#{ true_env }.log"
53
-
58
+
54
59
  `ssh -oStrictHostKeyChecking=no -tt #{ @config['cheftacular']['deploy_user'] }@#{ ip_address } "#{ @config['helper'].sudo(ip_address) } tail -f #{ log_loc } #{ get_tail_grep_string(pattern_to_match) }" > /dev/tty`
55
60
  end
56
61
 
57
62
  def get_tail_grep_string pattern_to_match=''
58
- "| grep -i -E #{ pattern_to_match }" unless pattern_to_match.blank?
63
+ "| grep -i -E \\\"#{ pattern_to_match }\\\"" unless pattern_to_match.blank?
59
64
  end
60
65
  end
61
66
  end
@@ -39,7 +39,7 @@ class Cheftacular
39
39
  if @config['helper'].is_initialization_command?(ARGV[0])
40
40
  @options['command'] = ARGV[0] #this is normally set by parse_context but that is not run for initialization commands
41
41
  else
42
- @config['stateless_action'].cheftacular_config('sync') unless @config['helper'].running_on_chef_node?
42
+ #@config['stateless_action'].cheftacular_config('sync') unless @config['helper'].running_on_chef_node?
43
43
 
44
44
  @config['stateless_action'].initialize_data_bag_contents(@options['env']) #ensure basic structure are always maintained before each run
45
45
 
@@ -7,11 +7,15 @@ class Cheftacular
7
7
 
8
8
  #public address, private address
9
9
  def parse_addresses_from_server_create_hash server_hash
10
+ ap server_hash
10
11
  case @options['preferred_cloud']
11
12
  when 'rackspace'
12
13
  [server_hash['ipv4_address'], server_hash['addresses']['private'][0]['addr']]
13
14
  when 'digitalocean'
14
- [server_hash['public_ip_address'], server_hash['private_ip_address']]
15
+ [
16
+ server_hash['networks']['v4'].select { |hash| hash['type'] == 'public' }.first['ip_address'],
17
+ server_hash['networks']['v4'].select { |hash| hash['type'] == 'private' }.first['ip_address']
18
+ ]
15
19
  else raise "CRITICAL! Encountered unsupported preferred cloud #{ @options['preferred_cloud'] }"
16
20
  end
17
21
  end
@@ -62,7 +62,7 @@ class Cheftacular
62
62
  record_hash['SRV_weight'],
63
63
  record_hash['SRV_port'],
64
64
  record_hash['SRV_target'],
65
- (record_hash['activate_cloudflare'] ? 0 : 1) #service_mode
65
+ (record_hash['activate_cloudflare'] ? 1 : 0) #service_mode
66
66
  )
67
67
 
68
68
  print 'create'.ljust(20, '_')
@@ -20,10 +20,10 @@ class Cheftacular
20
20
  nodes
21
21
  end
22
22
 
23
- def exception_output message, exception='', exit_on_call=true, suppress_error_output=false
23
+ def exception_output message, exception=nil, exit_on_call=true, suppress_error_output=false
24
24
  puts "#{ message }\n"
25
25
 
26
- puts("Error message: #{ exception }\n#{ exception.backtrace.join("\n") }") unless suppress_error_output
26
+ puts("Error message: #{ exception }\n#{ exception.backtrace.join("\n") }") if !exception.nil? && !suppress_error_output
27
27
 
28
28
  exit if exit_on_call
29
29
  end
@@ -89,6 +89,8 @@ class Cheftacular
89
89
  FileUtils.rm_rf("#{ base_dir }/#{ entry }") if File.exists?("#{ base_dir }/#{ entry }") && File.directory?("#{ base_dir }/#{ entry }")
90
90
  when 'current-audit-only'
91
91
  FileUtils.rm("#{ base_dir }/#{ entry }") if File.file?("#{ base_dir }/#{ entry }") && entry.include?(Time.now.strftime("%Y%m%d"))
92
+ else
93
+ puts "Warning, received unknown mode (#{ mode })! Please post your logs as an issue on github!"
92
94
  end
93
95
 
94
96
  if File.exists?("#{ base_dir }/#{ entry }") && File.directory?("#{ base_dir }/#{ entry }")
@@ -135,6 +137,54 @@ class Cheftacular
135
137
  File.open( File.join(@config['locs']['chef-repo'], "config", to_be_created_filename), "w") { |f| f.write(File.read(File.join(@config['locs']['examples'], example_filename))) }
136
138
  end
137
139
 
140
+ def parse_latest_berkshelf_cookbook_versions berkshelf_cookbooks={}
141
+
142
+ Dir.foreach(@config['locs']['berks']) do |berkshelf_cookbook|
143
+ next if is_junk_filename?(berkshelf_cookbook)
144
+ use_current_cookbook = false
145
+
146
+ true_cookbook_name = berkshelf_cookbook.rpartition('-').first
147
+ cookbook_version = parse_version_from_berkshelf_cookbook(berkshelf_cookbook)
148
+
149
+ #get only the latest version, berkshelf pulls in multiple commits from git repos for SOME REASON
150
+ use_current_cookbook = !berkshelf_cookbooks.has_key?(true_cookbook_name)
151
+ use_current_cookbook = @config['helper'].is_higher_version?(cookbook_version, berkshelf_cookbooks[true_cookbook_name]['version']) if berkshelf_cookbooks.has_key?(true_cookbook_name)
152
+
153
+ if use_current_cookbook
154
+ berkshelf_cookbooks[true_cookbook_name] ||= {}
155
+ berkshelf_cookbooks[true_cookbook_name]['version'] = cookbook_version
156
+ berkshelf_cookbooks[true_cookbook_name]['location'] = berkshelf_cookbook
157
+ berkshelf_cookbooks[true_cookbook_name]['mtime'] = File.mtime(File.expand_path("#{ @config['locs']['berks'] }/#{ berkshelf_cookbook }"))
158
+ end
159
+ end
160
+
161
+ berkshelf_cookbooks
162
+ end
163
+
164
+ def parse_chef_repo_cookbook_versions chef_repo_cookbooks={}
165
+ Dir.foreach(@config['locs']['cookbooks']) do |chef_repo_cookbook|
166
+ next if is_junk_filename?(chef_repo_cookbook)
167
+
168
+ new_name = chef_repo_cookbook.rpartition('-').first
169
+
170
+ chef_repo_cookbooks[chef_repo_cookbook] = if File.exists?(File.expand_path("#{ @config['locs']['cookbooks'] }/#{ chef_repo_cookbook }/metadata.rb"))
171
+ File.read(File.expand_path("#{ @config['locs']['cookbooks'] }/#{ chef_repo_cookbook }/metadata.rb")).gsub('"',"'").gsub(/^version[\s]*('\d[.\d]+')/).peek[/('\d[.\d]+')/].gsub("'",'')
172
+ else
173
+ JSON.parse(File.read(File.expand_path("#{ @config['locs']['cookbooks'] }/#{ chef_repo_cookbook }/metadata.json"))).to_hash['version']
174
+ end
175
+ end
176
+
177
+ chef_repo_cookbooks
178
+ end
179
+
180
+ def parse_version_from_berkshelf_cookbook berkshelf_cookbook
181
+ if File.exists?(File.expand_path("#{ @config['locs']['berks'] }/#{ berkshelf_cookbook }/metadata.rb"))
182
+ File.read(File.expand_path("#{ @config['locs']['berks'] }/#{ berkshelf_cookbook }/metadata.rb")).gsub('"',"'").gsub(/^version[\s]*('\d[.\d]+')/).peek[/('\d[.\d]+')/].gsub("'",'')
183
+ else
184
+ berkshelf_cookbook.split('-').last
185
+ end
186
+ end
187
+
138
188
  private
139
189
  def current_file_path file_name, use_timestamp=true
140
190
  File.join( @config['locs']['app-root'], 'tmp', @config['helper'].declassify, ( use_timestamp ? "#{ Time.now.strftime("%Y%m%d") }-#{ file_name }" : file_name ))
@@ -23,7 +23,7 @@ class Cheftacular
23
23
  true_obj = if !file_cache_nodes.empty? && @config['parser'].array_of_nodes_contains_node_name?(file_cache_nodes, n.name)
24
24
  file_cache_nodes[@config['parser'].index_of_node_name_in_array_of_nodes(file_cache_nodes, n.name)]
25
25
  else
26
- @config['filesystem'].cleanup_file_caches('current')
26
+ @config['filesystem'].cleanup_file_caches('current-nodes')
27
27
 
28
28
  @config['ridley'].node.find(n.name)
29
29
  end
@@ -30,8 +30,8 @@ class Cheftacular
30
30
 
31
31
  def is_initialization_command? command=''
32
32
  command ||= ''
33
-
34
- @config['initialization_action'].public_methods(false).include?(command.to_sym)
33
+
34
+ @config['initialization_action'].public_methods(false).include?(command.to_sym) || command.blank?
35
35
  end
36
36
 
37
37
  def running_on_chef_node? ret = false
@@ -140,12 +140,13 @@ class Cheftacular
140
140
  def compile_documentation_lines mode, out=[]
141
141
  doc_arr = case mode
142
142
  when 'action' then @config['documentation']['action']
143
- when 'application' then @config['documentation']['action'] + @config['documentation']['application']
143
+ when 'application' then @config['documentation']['application'].merge(@config['documentation']['action'])
144
144
  when 'stateless_action' then @config['documentation']['stateless_action']
145
- when 'devops' then @config['documentation']['action'] + @config['documentation']['stateless_action']
145
+ when 'devops' then @config['documentation']['stateless_action'].merge(@config['documentation']['action'])
146
146
  end
147
147
 
148
- count = 1
148
+ doc_arr = doc_arr.to_a.map { |doc| doc[1]['long_description'] }
149
+ count = 1
149
150
 
150
151
  doc_arr.sort {|a, b| a[0] <=> b[0]}.flatten(1).each do |line|
151
152
  out << "#{ count }. #{ line }" if line.class.to_s == 'String'
@@ -158,6 +159,12 @@ class Cheftacular
158
159
  out
159
160
  end
160
161
 
162
+ def compile_short_context_descriptions documentation_hash, padding_length=25, out=[]
163
+ out << documentation_hash.to_a.map { |doc| "#{ doc[0].to_s.ljust(padding_length, '_') }_#{ doc[1]['short_description'] }" }
164
+
165
+ out.flatten.sort {|a, b| a[0] <=> b[0]}.join("\n\n")
166
+ end
167
+
161
168
  #compares how close str1 is to str2
162
169
  def compare_strings str1, str2
163
170
  str1_chars = str1.split('').uniq
@@ -260,6 +267,28 @@ class Cheftacular
260
267
 
261
268
  puts("Completed rvm.sh installation into /etc/profile.d/rvm.sh") unless @options['quiet']
262
269
  end
270
+
271
+ def send_log_bag_hash_slack_notification logs_bag_hash, method, on_failing_exit_status_message=''
272
+ if @config['cheftacular']['slack']['webhook']
273
+ logs_bag_hash.each_pair do |key, hash|
274
+ next unless key.include?(method.to_s)
275
+
276
+ if hash['exit_status'] && hash['exit_status'] == 1
277
+ @config['stateless_action'].slack(hash['text'].prepend('```').insert(-1, '```'))
278
+
279
+ @config['error'].exception_output(on_failing_exit_status_message) if !on_failing_exit_status_message.blank?
280
+ end
281
+ end
282
+ end
283
+ end
284
+
285
+ def slack_current_deploy_arguments
286
+ msg = "#{ Socket.gethostname } just set for the repository #{ @config['getter'].get_repository_from_role_name(@options['role']) }:\n"
287
+ msg << "the organization to #{ @options['deploy_organization'] }\n" if @options['deploy_organization']
288
+ msg << "the revision to #{ @options['target_revision'] }" if @options['target_revision']
289
+
290
+ @config['stateless_action'].slack(msg.prepend('```').insert(-1, '```'), @config['cheftacular']['slack']['notify_on_deployment_args'])
291
+ end
263
292
  end
264
293
  end
265
294
 
@@ -253,25 +253,9 @@ class Cheftacular
253
253
 
254
254
  @config['ChefDataBag'].init_bag('default', 'cheftacular', false)
255
255
 
256
- diff_hash = @config['cheftacular'].deep_diff(@config['default']['cheftacular_bag_hash'], true).except('mode', 'default_repository').compact
256
+ @config['initial_cheftacular_yml'] = @config['cheftacular'].deep_dup
257
257
 
258
- diff_hash.each_pair do |key, value|
259
- diff_hash.delete(key) if value.empty? || value.nil?
260
- end
261
-
262
- if @config['helper'].running_in_mode?('devops') && !diff_hash.empty?
263
- puts "Difference detected between local cheftacular.yml and data bag cheftacular.yml! Displaying..."
264
-
265
- ap diff_hash
266
- elsif @config['helper'].running_in_mode?('application') && @config['default']['cheftacular_bag_hash']['slack']['webhook'] && !diff_hash.empty?
267
- @config['slack_queue'] << diff_hash.awesome_inspect({plain: true, indent: 2}).prepend('```').insert(-1, '```')
268
- end
269
-
270
- @config['cheftacular'] = if @config['default']['cheftacular_bag_hash']['sync_application_cheftacular_yml']
271
- @config['default']['cheftacular_bag_hash'].deep_merge(@config['cheftacular'])
272
- else
273
- @config['cheftacular'].deep_merge(@config['default']['cheftacular_bag_hash'].except('default_repository', 'mode'))
274
- end
258
+ @config['cheftacular'] = @config['default']['cheftacular_bag_hash'].deep_merge(@config['cheftacular'])
275
259
  end
276
260
 
277
261
  def initialize_default_cheftacular_options
@@ -299,10 +283,10 @@ class Cheftacular
299
283
  def initialize_documentation_hash
300
284
  @config['documentation'] ||= {}
301
285
  @config['documentation']['arguments'] ||= []
302
- @config['documentation']['action'] ||= []
303
- @config['documentation']['stateless_action'] ||= []
304
- @config['documentation']['application'] ||= []
305
- @config['documentation']['devops'] ||= []
286
+ @config['documentation']['action'] ||= {}
287
+ @config['documentation']['stateless_action'] ||= {}
288
+ @config['documentation']['application'] ||= {}
289
+ @config['documentation']['devops'] ||= {}
306
290
  end
307
291
 
308
292
  def initialize_locations
@@ -419,8 +403,10 @@ class Cheftacular
419
403
  @config['ruby_string'] = "ruby-" + @config['ruby_string'] unless @config['ruby_string'].include?('ruby-')
420
404
 
421
405
  #TODO Reevaluate for non-rvm setups
422
- @config['bundle_command'] = "/home/#{ @config['cheftacular']['deploy_user'] }/.rvm/gems/#{ @config['ruby_string'].chomp }@global/bin/bundle"
423
- @config['ruby_command'] = "/home/#{ @config['cheftacular']['deploy_user'] }/.rvm/rubies/#{ @config['ruby_string'].chomp }/bin/ruby"
406
+ @config['bundle_command'] = "/home/#{ @config['cheftacular']['deploy_user'] }/.rvm/gems/#{ @config['ruby_string'].chomp }@global/bin/bundle"
407
+ @config['ruby_command'] = "/home/#{ @config['cheftacular']['deploy_user'] }/.rvm/rubies/#{ @config['ruby_string'].chomp }/bin/ruby"
408
+ @config['internal_ruby_config'] = File.expand_path(__FILE__)[/(ruby\-[\d\.@\w]+)/]
409
+ #@config['internal_ruby_version'] = @config['internal_ruby_config'][/([\d\.]+)/] #reactivate when needed
424
410
  end
425
411
 
426
412
  def initialize_passwords env, refresh_bag=false
@@ -448,7 +434,11 @@ class Cheftacular
448
434
  if @config['helper'].is_higher_version? detected_version, current_version
449
435
  puts "\n Your Cheftacular is out of date. Currently #{ current_version } and remote version is #{ detected_version }.\n"
450
436
 
451
- puts "Please update the gemfile to #{ detected_version }, bundle install and then restart this process.\n"
437
+ if @config['internal_ruby_config'].include?('@global')
438
+ puts "Please run rvm #{ @config['internal_ruby_config'] } do gem update cheftacular to update to the latest version"
439
+ else
440
+ puts "Please update the gemfile to #{ detected_version }, bundle install and then restart this process.\n"
441
+ end
452
442
 
453
443
  exit
454
444
  else
@@ -113,6 +113,8 @@ class Cheftacular
113
113
  repo_state_hash['deploy_organization'] = nil
114
114
  end
115
115
 
116
+ @config['helper'].slack_current_deploy_arguments unless @config['cheftacular']['slack']['notify_on_deployment_args'].blank?
117
+
116
118
  @config['ChefDataBag'].save_config_bag
117
119
  end
118
120
 
@@ -2,7 +2,8 @@
2
2
  class Cheftacular
3
3
  class StatelessActionDocumentation
4
4
  def add_ssh_key_to_bag
5
- @config['documentation']['stateless_action'] << [
5
+ @config['documentation']['stateless_action'][__method__] ||= {}
6
+ @config['documentation']['stateless_action'][__method__]['long_description'] = [
6
7
  "`cft add_ssh_key_to_bag \"<NEW SSH PUB KEY>\" [SPECIFIC_REPOSITORY]` this command will add the given ssh key to the default authentication data bag. " +
7
8
  "After this your server recipes should read the contents of the 'default' 'authentication' bag for the authorized_keys array.",
8
9
 
@@ -12,6 +13,8 @@ class Cheftacular
12
13
  "does not exist in the cheftacular.yml respositories hash. You can then use this data to give users selective ssh access to certain servers."
13
14
  ]
14
15
  ]
16
+
17
+ @config['documentation']['stateless_action'][__method__]['short_description'] = 'Adds an ssh key to your authentication bag'
15
18
  end
16
19
  end
17
20
 
@@ -1,7 +1,7 @@
1
1
  class Cheftacular
2
2
  class StatelessActionDocumentation
3
3
  def arguments
4
- @config['documentation']['arguments'] << [
4
+ @config['documentation']['arguments'] << [
5
5
  '## Arguments and flags for cheftacular',
6
6
 
7
7
  '### Environment flags',
@@ -46,11 +46,16 @@ class Cheftacular
46
46
 
47
47
  '1. `-e|--except-role ROLE_NAME` will *prevent* any server with this role from being *deployed to* for the deploy command. Other commands will ignore this argument.',
48
48
 
49
- '2. `-z|--unset-revision` will unset a custom revision specified in the arg below and make the codebase utilize the default.',
49
+ '2. `-z|--unset-github-deploy-args` will unset a custom revision specified in the arg below and make the codebase utilize the default.',
50
50
 
51
51
  "3. `-Z|--revision REVISION` will force the role you're deploying to to utilize the revision specified here. This can be a specific commit, a branch name or even a tag.",
52
52
 
53
- ' 1. Note: The system does not check if the revision exists, if you pass a non-existent revision no one will be able to deploy to that role until -Z with a correction revision or -z is passed.'
53
+ ' 1. Note: The system does not check if the revision exists, if you pass a non-existent revision no one will be able to deploy to that role until -Z with a correction revision or -z is passed.',
54
+
55
+ "4. The `-O ORGANIZATION` flag can be used with TheCheftacularCookbook to set an *organization* your app can try " +
56
+ "deploying from, your git user needs access to these forks / organization(s).",
57
+
58
+ " 3. The `-z|--unset-github-deploy-args` option will clear your current `-Z` and `-O` flags."
54
59
  ]
55
60
  end
56
61
  end
@@ -2,7 +2,8 @@
2
2
  class Cheftacular
3
3
  class StatelessActionDocumentation
4
4
  def backups
5
- @config['documentation']['stateless_action'] << [
5
+ @config['documentation']['stateless_action'][__method__] ||= {}
6
+ @config['documentation']['stateless_action'][__method__]['long_description'] = [
6
7
  "`cft backups [activate|deactivate|load|restore]` this command " +
7
8
  "sets the fetch_backups and restore_backups flags in your config data bag for an environment. " +
8
9
  "These can be used to give application developers a way to trigger / untrigger restores in an environment",
@@ -19,7 +20,9 @@ class Cheftacular
19
20
  ]
20
21
  ]
21
22
 
22
- @config['documentation']['application'] << @config['documentation']['stateless_action'].last
23
+ @config['documentation']['stateless_action'][__method__]['short_description'] = 'Runs various backup commands on your current environment'
24
+
25
+ @config['documentation']['application'][__method__] = @config['documentation']['stateless_action'][__method__]
23
26
  end
24
27
  end
25
28
 
@@ -1,11 +1,14 @@
1
1
  class Cheftacular
2
2
  class StatelessActionDocumentation
3
3
  def ubuntu_bootstrap
4
- @config['documentation']['stateless_action'] << [
4
+ @config['documentation']['stateless_action'][__method__] ||= {}
5
+ @config['documentation']['stateless_action'][__method__]['long_description'] = [
5
6
  "`cft ubuntu_bootstrap ADDRESS ROOT_PASS` This command will bring a fresh server to a state " +
6
7
  "where chef-client can be run on it via `cft chef-bootstrap`. It should be noted that it is in "+
7
8
  "this step where a server's randomized deploy_user sudo password is generated."
8
9
  ]
10
+
11
+ @config['documentation']['stateless_action'][__method__]['short_description'] = 'Updates an ubuntu server to have more secure defaults'
9
12
  end
10
13
  end
11
14
 
@@ -85,6 +88,7 @@ class Cheftacular
85
88
 
86
89
  deploy_commands.each do |cmnd|
87
90
  puts("(#{ @options['address'] }) Running #{ cmnd.gsub("#{ new_deploy_pass }", "sudo password") }") unless @options['quiet'] || @options['in_scaling']
91
+
88
92
  out << `ssh -t -oStrictHostKeyChecking=no #{ deploy_user }@#{ @options['address'] } "#{ cmnd }"`
89
93
 
90
94
  puts(out.last) unless @options['quiet'] || @options['in_scaling']
@@ -94,6 +98,7 @@ class Cheftacular
94
98
 
95
99
  final_commands.each do |cmnd|
96
100
  puts "(#{ @options['address'] }) Running #{ cmnd.gsub("#{ new_deploy_pass }", "sudo password") }"
101
+
97
102
  out << `ssh -t -oStrictHostKeyChecking=no #{ deploy_user }@#{ @options['address'] } "#{ cmnd }"`
98
103
 
99
104
  puts(out.last) unless @options['quiet'] || @options['in_scaling']
@@ -101,11 +106,9 @@ class Cheftacular
101
106
 
102
107
  puts("Finished ruby setup......stage 3 of 3 for server #{ @options['address'] }") if @options['in_scaling']
103
108
 
104
- @config[@options['env']]['server_passwords_bag_hash']["#{ @options['address'] }-root-pass"] = @options['client_pass']
105
-
109
+ @config[@options['env']]['server_passwords_bag_hash']["#{ @options['address'] }-root-pass"] = @options['client_pass']
106
110
  @config[@options['env']]['server_passwords_bag_hash']["#{ @options['address'] }-deploy-pass"] = new_deploy_pass
107
-
108
- @config[@options['env']]['server_passwords_bag_hash']["#{ @options['address'] }-name"] = @options['node_name'] if @options['node_name']
111
+ @config[@options['env']]['server_passwords_bag_hash']["#{ @options['address'] }-name"] = @options['node_name'] if @options['node_name']
109
112
 
110
113
  @config['ChefDataBag'].save_server_passwords_bag unless @options['in_scaling']
111
114
  end
@@ -2,10 +2,13 @@
2
2
  class Cheftacular
3
3
  class StatelessActionDocumentation
4
4
  def check_cheftacular_yml_keys
5
- @config['documentation']['stateless_action'] << [
5
+ @config['documentation']['stateless_action'][__method__] ||= {}
6
+ @config['documentation']['stateless_action'][__method__]['long_description'] = [
6
7
  "`cft check_cheftacular_yml_keys` allows you to check to see if your cheftacular yml keys are valid to the current version of cheftacular. " +
7
8
  "It will also set your missing keys to their likely default and let you know to update the cheftacular.yml file."
8
9
  ]
10
+
11
+ @config['documentation']['stateless_action'][__method__]['short_description'] = "Makes sure your cheftacular.yml has up-to-date keys for #{ Cheftacular::VERSION }"
9
12
  end
10
13
  end
11
14
 
@@ -13,6 +16,17 @@ class Cheftacular
13
16
  def check_cheftacular_yml_keys out=[], exit_on_missing=false, warn_on_missing=false
14
17
  base_message = "Your cheftacular.yml is missing the key KEY, its default value is being set to DEFAULT for this run."
15
18
 
19
+ #############################2.9.0################################################
20
+
21
+ unless @config['cheftacular']['slack'].has_key?('notify_on_deployment_args')
22
+ #backup_config:global_backup_role_name
23
+ base_message.gsub('KEY', 'notify_on_deployment_args').gsub('DEFAULT', 'false')
24
+
25
+ @config['cheftacular']['slack']['notify_on_deployment_args'] = false
26
+
27
+ warn_on_missing = true
28
+ end
29
+
16
30
  #############################2.7.0################################################
17
31
 
18
32
  unless @config['cheftacular'].has_key?('backup_config')
@@ -2,10 +2,17 @@
2
2
  class Cheftacular
3
3
  class StatelessActionDocumentation
4
4
  def chef_bootstrap
5
- @config['documentation']['stateless_action'] << [
5
+ @config['documentation']['stateless_action'][__method__] ||= {}
6
+ @config['documentation']['stateless_action'][__method__]['long_description'] = [
6
7
  "`cft chef_bootstrap ADDRESS NODE_NAME` allows you to register a node in the chef system, " +
7
- "remove any lingering data that may be associated with it and update the node's runlist if it has an entry in nodes_dir for its NODE_NAME."
8
+ "remove any lingering data that may be associated with it and update the node's runlist if it has an entry in nodes_dir for its NODE_NAME.",
9
+
10
+ [
11
+ " 1. This command is part of the `cft full_bootstrap` command"
12
+ ]
8
13
  ]
14
+
15
+ @config['documentation']['stateless_action'][__method__]['short_description'] = 'Bootstraps basic chef properties on the target server'
9
16
  end
10
17
  end
11
18
 
@@ -1,7 +1,8 @@
1
1
  class Cheftacular
2
2
  class StatelessActionDocumentation
3
3
  def chef_server
4
- @config['documentation']['stateless_action'] << [
4
+ @config['documentation']['stateless_action'][__method__] ||= {}
5
+ @config['documentation']['stateless_action'][__method__]['long_description'] = [
5
6
  "`cft chef_server [restart|processes|memory]` this command can be used to query the chef server for stats if the cheftacular.yml " +
6
7
  "has the chef_server key filled out. Useful for low resource chef-servers.",
7
8
 
@@ -20,7 +21,9 @@ class Cheftacular
20
21
  ]
21
22
  ]
22
23
 
23
- @config['documentation']['application'] << @config['documentation']['stateless_action'].last
24
+ @config['documentation']['stateless_action'][__method__]['short_description'] = 'Allows you to check the state of the primary Chef server'
25
+
26
+ @config['documentation']['application'][__method__] = @config['documentation']['stateless_action'][__method__]
24
27
  end
25
28
  end
26
29