cheftacular 2.10.2 → 2.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/lib/cheftacular/README.md +119 -77
  3. data/lib/cheftacular/actions/check.rb +58 -24
  4. data/lib/cheftacular/actions/deploy.rb +5 -3
  5. data/lib/cheftacular/auditor.rb +4 -3
  6. data/lib/cheftacular/cheftacular.rb +1 -0
  7. data/lib/cheftacular/cloud_provider.rb +17 -12
  8. data/lib/cheftacular/file_system.rb +20 -0
  9. data/lib/cheftacular/getter.rb +1 -1
  10. data/lib/cheftacular/helper.rb +41 -28
  11. data/lib/cheftacular/initializer.rb +5 -3
  12. data/lib/cheftacular/parser.rb +10 -6
  13. data/lib/cheftacular/queue_master.rb +24 -0
  14. data/lib/cheftacular/stateless_actions/backups.rb +2 -2
  15. data/lib/cheftacular/stateless_actions/bootstrappers/{centos_bootstrap.rb → centos_bootstrap_from_queue.rb} +1 -1
  16. data/lib/cheftacular/stateless_actions/bootstrappers/{coreos_bootstrap.rb → coreos_bootstrap_from_queue.rb} +1 -1
  17. data/lib/cheftacular/stateless_actions/bootstrappers/{fedora_bootstrap.rb → fedora_bootstrap_from_queue.rb} +1 -1
  18. data/lib/cheftacular/stateless_actions/bootstrappers/{redhat_bootstrap.rb → redhat_bootstrap_from_queue.rb} +1 -1
  19. data/lib/cheftacular/stateless_actions/bootstrappers/ubuntu_bootstrap_from_queue.rb +203 -0
  20. data/lib/cheftacular/stateless_actions/bootstrappers/vyatta_bootstrap_from_queue.rb +7 -0
  21. data/lib/cheftacular/stateless_actions/check_cheftacular_yml_keys.rb +30 -0
  22. data/lib/cheftacular/stateless_actions/chef_bootstrap_from_queue.rb +89 -0
  23. data/lib/cheftacular/stateless_actions/cheftacular_yml_help.rb +55 -6
  24. data/lib/cheftacular/stateless_actions/client_list.rb +1 -3
  25. data/lib/cheftacular/stateless_actions/cloud_bootstrap.rb +24 -15
  26. data/lib/cheftacular/stateless_actions/cloud_bootstrap_from_queue.rb +37 -0
  27. data/lib/cheftacular/stateless_actions/environment.rb +80 -31
  28. data/lib/cheftacular/stateless_actions/fix_known_hosts.rb +1 -22
  29. data/lib/cheftacular/stateless_actions/{full_bootstrap.rb → full_bootstrap_from_queue.rb} +8 -15
  30. data/lib/cheftacular/stateless_actions/knife_upload.rb +14 -3
  31. data/lib/cheftacular/stateless_actions/reinitialize.rb +1 -1
  32. data/lib/cheftacular/stateless_actions/remove_client.rb +3 -3
  33. data/lib/cheftacular/stateless_actions/restart_swap.rb +0 -1
  34. data/lib/cheftacular/stateless_actions/test_env.rb +24 -18
  35. data/lib/cheftacular/stateless_actions/update_thecheftacularcookbook.rb +1 -1
  36. data/lib/cheftacular/stateless_actions/upload_nodes.rb +2 -2
  37. data/lib/cheftacular/version.rb +1 -1
  38. data/lib/cloud_interactor/domain/create.rb +2 -2
  39. data/lib/cloud_interactor/domain/create_record.rb +1 -1
  40. data/lib/cloud_interactor/domain/destroy.rb +2 -2
  41. data/lib/cloud_interactor/domain/destroy_record.rb +1 -1
  42. data/lib/cloud_interactor/domain/list_records.rb +1 -1
  43. data/lib/cloud_interactor/domain/read.rb +1 -1
  44. data/lib/cloud_interactor/domain/read_record.rb +1 -1
  45. data/lib/cloud_interactor/domain/update.rb +2 -2
  46. data/lib/cloud_interactor/domain/update_record.rb +2 -2
  47. data/lib/cloud_interactor/helpers.rb +11 -6
  48. data/lib/cloud_interactor/server/attach_volume.rb +1 -1
  49. data/lib/cloud_interactor/server/create.rb +4 -4
  50. data/lib/cloud_interactor/server/detach_volume.rb +2 -2
  51. data/lib/cloud_interactor/server/list_volumes.rb +1 -1
  52. data/lib/cloud_interactor/server/poll.rb +2 -2
  53. data/lib/cloud_interactor/server/read_volume.rb +1 -1
  54. data/lib/cloud_interactor/version.rb +1 -1
  55. data/lib/cloud_interactor/volume/create.rb +1 -1
  56. data/lib/sshkit/getters.rb +9 -1
  57. metadata +25 -10
  58. data/lib/cheftacular/stateless_actions/bootstrappers/ubuntu_bootstrap.rb +0 -116
  59. data/lib/cheftacular/stateless_actions/bootstrappers/vyatta_bootstrap.rb +0 -7
  60. data/lib/cheftacular/stateless_actions/chef_bootstrap.rb +0 -63
@@ -1,6 +1,6 @@
1
1
  class Cheftacular
2
2
  class StatelessAction
3
- def redhat_bootstrap out=[]
3
+ def redhat_bootstrap_from_queue out=[]
4
4
  raise "Not yet implemented!"
5
5
  end
6
6
  end
@@ -1,6 +1,6 @@
1
1
  class Cheftacular
2
2
  class StatelessAction
3
- def coreos_bootstrap out=[]
3
+ def coreos_bootstrap_from_queue out=[]
4
4
  raise "Not yet implemented!"
5
5
  end
6
6
  end
@@ -1,6 +1,6 @@
1
1
  class Cheftacular
2
2
  class StatelessAction
3
- def fedora_bootstrap out=[]
3
+ def fedora_bootstrap_from_queue out=[]
4
4
  raise "Not yet implemented!"
5
5
  end
6
6
  end
@@ -1,6 +1,6 @@
1
1
  class Cheftacular
2
2
  class StatelessAction
3
- def redhat_bootstrap out=[]
3
+ def redhat_bootstrap_from_queue out=[]
4
4
  raise "Not yet implemented!"
5
5
  end
6
6
  end
@@ -0,0 +1,203 @@
1
+ class Cheftacular
2
+ class StatelessActionDocumentation
3
+ def ubuntu_bootstrap_from_queue
4
+ @config['documentation']['stateless_action'][__method__] ||= {}
5
+ @config['documentation']['stateless_action'][__method__]['long_description'] = [
6
+ "`cft ubuntu_bootstrap_from_queue` This command will bring a fresh server to a state " +
7
+ "where chef-client can be run on it via `cft chef-bootstrap`. It should be noted that it is in "+
8
+ "this step where a server's randomized deploy_user sudo password is generated."
9
+ ]
10
+
11
+ @config['documentation']['stateless_action'][__method__]['short_description'] = '[Not Directly Callable]'
12
+ end
13
+ end
14
+
15
+ class StatelessAction
16
+ def ubuntu_bootstrap_from_queue threads=[], execution_hash_array=[]
17
+ raise "This action is not meant to be called directly!" if !@options['in_scaling'] && !@options['in_single_server_creation']
18
+
19
+ if `which sshpass`.empty?
20
+ raise "sshpass not installed! Please run brew install https://raw.github.com/eugeneoden/homebrew/eca9de1/Library/Formula/sshpass.rb (or get it from your repo for linux)"
21
+ end
22
+
23
+ @config['bootstrap_timestamp'] ||= Time.now.strftime("%Y%m%d%H%M%S")
24
+
25
+ @config['queue_master'].generate_passwords_for_each_server_hash_in_queue
26
+
27
+ execution_hash_array << compile_root_execute_hash
28
+ execution_hash_array << compile_deploy_execute_hash
29
+ execution_hash_array << compile_rvm_execute_hash if @config['cheftacular']['install_rvm_on_boot']
30
+ execution_hash_array << compile_install_rvm_sh_file_hashes if @config['cheftacular']['install_rvm_on_boot']
31
+ execution_hash_array = execution_hash_array.flatten(1)
32
+
33
+ @config['server_creation_queue'].each do |server_hash|
34
+ puts("#{ server_name_output(server_hash) }_Starting initial setup for server...") if @options['in_scaling']
35
+
36
+ threads << Thread.new { execute_execution_hash_array(server_hash, execution_hash_array) }
37
+ end
38
+
39
+ threads.each { |thread| thread.join }
40
+
41
+ @config['server_creation_queue'].each do |server_hash|
42
+ @config[@options['env']]['server_passwords_bag_hash']["#{ server_hash['address'] }-root-pass"] = server_hash['client_pass']
43
+ @config[@options['env']]['server_passwords_bag_hash']["#{ server_hash['address'] }-deploy-pass"] = server_hash['deploy_password']
44
+ @config[@options['env']]['server_passwords_bag_hash']["#{ server_hash['address'] }-name"] = server_hash['node_name']
45
+ end
46
+
47
+ @config['ChefDataBag'].save_server_passwords_bag unless @config['in_server_creation_queue']
48
+ end
49
+
50
+ private
51
+
52
+ def compile_root_execute_hash
53
+ deploy_user = @config['cheftacular']['deploy_user']
54
+
55
+ root_commands = [
56
+ "cd /home",
57
+ "adduser #{ deploy_user } --gecos \",,,,\" --disabled-password",
58
+ "echo #{ deploy_user }:NEW_DEPLOY_PASS | chpasswd",
59
+ "adduser #{ deploy_user } www-data",
60
+ "adduser #{ deploy_user } sudo",
61
+ "mkdir -p /home/#{ deploy_user }/.ssh",
62
+ "touch /home/#{ deploy_user }/.ssh/authorized_keys && touch /home/#{ deploy_user }/.ssh/known_hosts",
63
+ "chown -R #{ deploy_user }:www-data /home/#{ deploy_user }/.ssh",
64
+ 'sed -i "s/StrictModes yes/StrictModes yes\nPasswordAuthentication no\nUseDNS no\nAllowUsers deploy postgres\n/" /etc/ssh/sshd_config'.gsub('deploy', deploy_user),
65
+ 'sed -i "s/PermitRootLogin yes/PermitRootLogin no/" /etc/ssh/sshd_config'
66
+ ]
67
+
68
+ @config['default']['authentication_bag_hash']['authorized_keys'].each do |line|
69
+ root_commands << "echo \"#{ line }\" >> /home/#{ deploy_user }/.ssh/authorized_keys"
70
+ end
71
+
72
+ {
73
+ run_as: 'ssh',
74
+ command: root_commands.join(' && ').insert(-1, ' && service ssh restart'),
75
+ retries: @config['cheftacular']['server_creation_tries'].to_i,
76
+ use_root_pass: true
77
+ }
78
+ end
79
+
80
+ def compile_deploy_execute_hash
81
+ sudo = "echo NEW_DEPLOY_PASS | sudo -S"
82
+
83
+ deploy_commands = [
84
+ "#{ sudo } apt-get update",
85
+ "#{ sudo } apt-get install curl #{ @config['cheftacular']['pre_install_packages'] } -y",
86
+ "#{ sudo } apt-get upgrade -y"
87
+ ]
88
+
89
+ if @config['cheftacular']['install_rvm_on_boot']
90
+ deploy_commands << "gpg --keyserver hkp://keys.gnupg.net --recv-keys #{ @config['cheftacular']['rvm_gpg_key'] }"
91
+ deploy_commands << "curl -L https://get.rvm.io | bash -s stable"
92
+ end
93
+
94
+ { run_as: 'ssh', command_array: deploy_commands }
95
+ end
96
+
97
+ def compile_rvm_execute_hash
98
+ rvm_source = "source /home/#{ @config['cheftacular']['deploy_user'] }/.rvm/bin/rvm &&"
99
+
100
+ final_commands = [
101
+ "#{ rvm_source } echo NEW_DEPLOY_PASS | rvmsudo -S rvm requirements",
102
+ "#{ rvm_source } rvm install #{ @config['cheftacular']['ruby_version'] }",
103
+ "#{ rvm_source } rvm alias create default #{ @config['cheftacular']['ruby_version'] }",
104
+ "#{ rvm_source } rvm gemset empty --force"
105
+ ]
106
+
107
+ final_commands << "#{ rvm_source } rvm install 1.9.3-p327" if @config['cheftacular']['chef_version'].to_i < 12
108
+
109
+ { run_as: 'ssh', command_array: final_commands }
110
+ end
111
+
112
+ def compile_install_rvm_sh_file_hashes ret_array=[]
113
+ sudo = "echo NEW_DEPLOY_PASS | sudo -S"
114
+
115
+ ssh_commands = [
116
+ "#{ sudo } mv /home/#{ @config['cheftacular']['deploy_user'] }/rvm.sh /etc/profile.d",
117
+ "#{ sudo } chmod 755 /etc/profile.d/rvm.sh",
118
+ "#{ sudo } chown root:root /etc/profile.d/rvm.sh"
119
+ ]
120
+
121
+ ret_array << { run_as: 'scp', upload: "#{ @config['locs']['cheftacular-lib-files'] }/rvm.sh", to: "/home/#{ @config['cheftacular']['deploy_user'] }"}
122
+ ret_array << { run_as: 'ssh', command: ssh_commands.join(' && ') }
123
+ ret_array
124
+ end
125
+
126
+ def execute_execution_hash_array server_hash, execution_array, output=[], create_logs=true, log_sub_dir='initial-setup'
127
+ execution_array.each do |execution_hash|
128
+ ssh_arguments = [server_hash['address']]
129
+ ssh_arguments << ( execution_hash.has_key?(:use_root_pass) ? 'root' : @config['cheftacular']['deploy_user'] )
130
+ ssh_arguments << [{ password: server_hash['client_pass'] }] if execution_hash.has_key?(:use_root_pass)
131
+ begin
132
+ case execution_hash[:run_as]
133
+ when 'ssh'
134
+ if execution_hash.has_key?(:command)
135
+ Net::SSH.start(*ssh_arguments.flatten) do |ssh|
136
+ output << ssh.exec!(replace_placeholders_in_command(server_hash, execution_hash[:command]))
137
+ end
138
+ end
139
+
140
+ if execution_hash.has_key?(:command_array)
141
+ execution_hash[:command_array].each do |command|
142
+ puts("#{ server_name_output(server_hash) }_Preparing to execute #{ command }")
143
+
144
+ Net::SSH.start(*ssh_arguments.flatten) do |ssh|
145
+ output << ssh.exec!(replace_placeholders_in_command(server_hash, command))
146
+ end
147
+ end
148
+ end
149
+ when 'scp'
150
+ if execution_hash.has_key?(:upload) && execution_hash.has_key?(:to)
151
+ puts("#{ server_name_output(server_hash) }_Preparing to upload #{ execution_hash[:upload] } to #{ execution_hash[:to] }")
152
+
153
+ Net::SCP.upload!(server_hash['address'], @config['cheftacular']['deploy_user'], execution_hash[:upload], execution_hash[:to])
154
+ end
155
+ when 'raw'
156
+ if execution_hash.has_key?(:command)
157
+ output << `#{ replace_placeholders_in_command(server_hash, execution_hash[:command]) }`
158
+ end
159
+
160
+ if execution_hash.has_key?(:command_array)
161
+ execution_hash[:command_array].each do |command|
162
+ puts("#{ server_name_output(server_hash) }_Preparing to execute #{ command }")
163
+
164
+ output << `#{ replace_placeholders_in_command(server_hash, command) }`
165
+ end
166
+ end
167
+ end
168
+ rescue Net::SSH::HostKeyMismatch => e
169
+ puts "#{ server_name_output(server_hash) }_Has a host key mismatch! Rewriting known_hosts file..."
170
+
171
+ @config['filesystem'].scrub_from_known_hosts(server_hash['address'])
172
+
173
+ sleep 15
174
+
175
+ retry
176
+ rescue StandardError => e
177
+ puts "#{ server_name_output(server_hash) }_@@@@@@@@@@@ Failing execution hash because of #{ e }!@@@@@@@@@@"
178
+ puts "#{ e.backtrace.join("\n") }" if @options['verbose']
179
+
180
+ if execution_hash.has_key?(:retries)
181
+ execution_hash[:retries] = execution_hash[:retries] -= 1
182
+ puts "There are #{ execution_hash[:retries] } tries left to evaluate the command."
183
+
184
+ sleep 60
185
+
186
+ raise "#{ server_name_output(server_hash) }_@@@@@@@@@@@ Unable to complete setup process!@@@@@@@@@@" if execution_hash[:retries] <= 0
187
+ retry
188
+ end
189
+ end
190
+ end
191
+
192
+ File.open("#{ @config['locs']['chef-log'] }/server-setup/#{ server_hash['node_name'] }-#{ log_sub_dir }-#{ @config['bootstrap_timestamp'] }.txt", 'a+') { |f| f.write(output.join("\n").scrub_pretty_text) }
193
+ end
194
+
195
+ def server_name_output server_hash
196
+ "#{ server_hash['node_name'] }".ljust(17,'_') + "#{ server_hash['address'] }".ljust(18,'_')
197
+ end
198
+
199
+ def replace_placeholders_in_command server_hash, command
200
+ command.gsub('NEW_DEPLOY_PASS', server_hash['deploy_password']).gsub('ADDRESS', server_hash['address']).gsub('CLIENT_PASS', server_hash['client_pass']).gsub('NODE_NAME', server_hash['node_name'])
201
+ end
202
+ end
203
+ end
@@ -0,0 +1,7 @@
1
+ class Cheftacular
2
+ class StatelessAction
3
+ def vyatta_bootstrap_from_queue out=[]
4
+ raise "Not yet implemented!"
5
+ end
6
+ end
7
+ end
@@ -16,6 +16,36 @@ class Cheftacular
16
16
  def check_cheftacular_yml_keys out=[], exit_on_missing=false, warn_on_missing=false
17
17
  base_message = "Your cheftacular.yml is missing the key KEY, its default value is being set to DEFAULT for this run."
18
18
 
19
+ #############################2.11.0################################################
20
+
21
+ unless @config['cheftacular'].has_key?('server_creation_tries')
22
+ @config['cheftacular']['server_creation_tries'] = 2
23
+ end
24
+
25
+ unless @config['cheftacular']['backup_config'].has_key?('db_primary_role')
26
+ puts base_message.gsub('KEY', 'backup_config:db_primary_role').gsub('DEFAULT', 'db_primary')
27
+
28
+ @config['cheftacular']['backup_config']['db_primary_role'] = 'db_primary'
29
+
30
+ warn_on_missing = true
31
+ end
32
+
33
+ unless @config['cheftacular'].has_key?('git')
34
+ puts(base_message.gsub('KEY', 'git').split(',').first + ', Please add the git high level key to your cheftacular.yml.')
35
+
36
+ @config['cheftacular']['git'] ||= {}
37
+
38
+ warn_on_missing = true
39
+ end
40
+
41
+ unless @config['cheftacular']['git'].has_key?('check_remote_for_branch_existence')
42
+ puts base_message.gsub('KEY', 'git:check_remote_for_branch_existence').gsub('DEFAULT', 'false')
43
+
44
+ @config['cheftacular']['git']['check_remote_for_branch_existence'] = false
45
+
46
+ warn_on_missing = true
47
+ end
48
+
19
49
  #############################2.10.0################################################
20
50
 
21
51
  unless @config['cheftacular'].has_key?('self_update_repository')
@@ -0,0 +1,89 @@
1
+
2
+ class Cheftacular
3
+ class StatelessActionDocumentation
4
+ def chef_bootstrap_from_queue
5
+ @config['documentation']['stateless_action'][__method__] ||= {}
6
+ @config['documentation']['stateless_action'][__method__]['long_description'] = [
7
+ "`cft chef_bootstrap_from_queue` allows you to register a node in the chef system, " +
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 and cannot be called directly"
12
+ ]
13
+ ]
14
+
15
+ @config['documentation']['stateless_action'][__method__]['short_description'] = 'Bootstraps basic chef properties on the target server'
16
+ end
17
+ end
18
+
19
+ class StatelessAction
20
+ def chef_bootstrap_from_queue threads=[]
21
+ raise "This action is not meant to be called directly!" if !@options['in_scaling'] && !@options['in_single_server_creation']
22
+
23
+ #@config['stateless_action'].remove_client #just in case
24
+
25
+ execution_hash_array = compile_chef_bootstrap_commands
26
+
27
+ @config['bootstrap_timestamp'] ||= Time.now.strftime("%Y%m%d%H%M%S")
28
+
29
+ @config['server_creation_queue'].each do |server_hash|
30
+ puts("(#{ server_hash['node_name'] })[#{ server_hash['address'] }] Starting chef-client installation...") unless @options['quiet']
31
+
32
+ threads << Thread.new { execute_execution_hash_array(server_hash, execution_hash_array) }
33
+ end
34
+
35
+ threads.each { |thread| thread.join }
36
+
37
+ @options['force_yes'] = true # have the upload_nodes grab the new nodes
38
+
39
+ @config['stateless_action'].upload_nodes(true)
40
+ end
41
+
42
+ private
43
+
44
+ def compile_chef_bootstrap_commands final_command=[]
45
+ sudo = "echo NEW_DEPLOY_PASS | sudo -S"
46
+
47
+ if @config['cheftacular']['chef_version'].to_i >= 12
48
+ commands = [
49
+ "curl -L https://www.opscode.com/chef/install.sh > ~/chef-install.sh",
50
+ "#{ sudo } bash /home/#{ @config['cheftacular']['deploy_user'] }/chef-install.sh",
51
+ "rm ~/chef-install.sh"
52
+ ]
53
+
54
+ final_command << { run_as: 'ssh', command_array: commands }
55
+ end
56
+
57
+ final_command << knife_bootstrap_command
58
+ final_command << export_data_bag_key_file_command
59
+ final_command << move_data_bag_key_file_command
60
+
61
+ final_command
62
+ end
63
+
64
+ def knife_bootstrap_command
65
+ user = @config['cheftacular']['deploy_user']
66
+ chef_ver = @config['cheftacular']['chef_version'].to_i >= 12 ? '12.4.0' : '11.16.4'
67
+
68
+ { run_as: 'raw', command: "knife bootstrap ADDRESS -x #{ user } -P NEW_DEPLOY_PASS -N NODE_NAME --sudo --use-sudo-password --bootstrap-version #{ chef_ver }" }
69
+ end
70
+
71
+ def export_data_bag_key_file_command
72
+ chef_loc, key_file, user, address = set_data_bag_key_variables
73
+
74
+ #"scp -oStrictHostKeyChecking=no #{ chef_loc }/#{ key_file } #{ user }@#{ address }:/home/#{ user }"
75
+ { run_as: 'scp', upload: "#{ chef_loc }/#{ key_file }", to: "/home/#{ user }" }
76
+ end
77
+
78
+ def move_data_bag_key_file_command
79
+ chef_loc, key_file, user, address = set_data_bag_key_variables
80
+
81
+ #"ssh -t -oStrictHostKeyChecking=no #{ user }@#{ address } \"#{ @config['helper'].sudo(address) } mv -f /home/#{ user }/#{ key_file } /etc/chef\""
82
+ { run_as: 'ssh', command: "echo NEW_DEPLOY_PASS | sudo -S mv -f /home/#{ user }/#{ key_file } /etc/chef" }
83
+ end
84
+
85
+ def set_data_bag_key_variables
86
+ [ @config['locs']['chef'], @config['cheftacular']['data_bag_key_file'], @config['cheftacular']['deploy_user'], 'ADDRESS' ]
87
+ end
88
+ end
89
+ end
@@ -4,28 +4,77 @@ class Cheftacular
4
4
  def cheftacular_yml_help
5
5
  @config['documentation']['stateless_action'][__method__] ||= {}
6
6
  @config['documentation']['stateless_action'][__method__]['long_description'] = [
7
- "[NYI]`cft cheftacular_yml_help KEY` this command" +
7
+ "`cft cheftacular_yml_help KEY` this command" +
8
8
  "allows you to get help on the meaning of each key in your cheftacular.yml overall config.",
9
9
 
10
10
  [
11
- " 1. This command can also by run with `cft yaml_help`."
11
+ " 1. This command can also by run with `cft yaml_help`.",
12
+
13
+ " 2. To examine nested keys, you can use colons inbetween the keys like cloud_authentication:rackspace:email"
12
14
  ]
13
15
  ]
14
16
 
15
- @config['documentation']['stateless_action'][__method__]['short_description'] = '[NYI]Gives help on the keys in your cheftacular.yml'
17
+ @config['documentation']['stateless_action'][__method__]['short_description'] = 'Gives help on the keys in your cheftacular.yml'
16
18
 
17
19
  @config['documentation']['application'][__method__] = @config['documentation']['stateless_action'][__method__]
18
20
  end
21
+
22
+ alias_method :yml_help, :cheftacular_yml_help
23
+ end
24
+
25
+ class InitializationAction
26
+ def cheftacular_yml_help
27
+
28
+ end
29
+
30
+ alias_method :yml_help, :cheftacular_yml_help
19
31
  end
20
32
 
21
33
  class StatelessAction
22
- def cheftacular_yml_help command=''
23
- raise "Not Yet Implemented"
34
+ def cheftacular_yml_help command='', key_nesting_array=[]
35
+ key_to_check = ARGV[1]
24
36
 
37
+ raise "This command requires a key to check the documentation, please enter one as the first argument" if key_to_check.nil?
38
+
39
+ key_to_check.split(':').each { |key| key_nesting_array << key }
40
+
41
+ doc_hash = YAML::load(ERB.new(IO.read(File.open(File.expand_path("#{ @config['locs']['doc'] }/cheftacular_yml_help.yml")))).result)
42
+
43
+ traverse_documentation_hash(doc_hash, key_nesting_array)
25
44
  end
26
45
 
27
- alias_method :yaml_help, :cheftacular_yml_help
46
+ alias_method :yml_help, :cheftacular_yml_help
28
47
 
29
48
  private
49
+
50
+ def traverse_documentation_hash doc_hash, key_array, index=0
51
+ if doc_hash.has_key?(key_array[index])
52
+ if doc_hash[key_array[index]].class == Hash && key_array.length-1 == index
53
+ if doc_hash[key_array[index]]['key_description'].nil?
54
+ puts "Missing documentation for key #{ key_array[index] }!"
55
+ else
56
+ puts doc_hash[key_array[index]]['key_description']
57
+ end
58
+ elsif doc_hash[key_array[index]].class != Hash && key_array.length-1 == index
59
+ puts doc_hash[key_array[index]]
60
+ elsif doc_hash[key_array[index]].class == Hash && key_array.length-1 != index
61
+ traverse_documentation_hash(doc_hash[key_array[index]], key_array, index+1)
62
+ else
63
+ puts "You attempted to traverse the documentation with a value that was not a key to a hash (#{ key_array[index] }), the value of this key is:"
64
+
65
+ puts doc_hash[key_array[index]]
66
+
67
+ puts "If this is not what you were searching for, please verify your config tree with `cft cheftacular_config display`"
68
+ end
69
+ elsif doc_hash.has_key?('STAR_MATCHER') && key_array.length-1 != index
70
+ traverse_documentation_hash(doc_hash['STAR_MATCHER'], key_array, index+1)
71
+ elsif doc_hash.has_key?('STAR_MATCHER') && key_array.length-1 == index
72
+ puts doc_hash['STAR_MATCHER']['key_description']
73
+ else
74
+ puts "The documentation hash does not have the key #{ key_array[index] }, the keys available here are:"
75
+
76
+ ap doc_hash.keys
77
+ end
78
+ end
30
79
  end
31
80
  end