cheftacular 2.5.0 → 2.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/lib/cheftacular/README.md +155 -63
  3. data/lib/cheftacular/actions/db_console.rb +3 -3
  4. data/lib/cheftacular/actions/deploy.rb +18 -12
  5. data/lib/cheftacular/actions/migrate.rb +1 -1
  6. data/lib/cheftacular/actions/tail.rb +1 -1
  7. data/lib/cheftacular/auditor.rb +14 -6
  8. data/lib/cheftacular/chef/data_bag.rb +1 -4
  9. data/lib/cheftacular/cheftacular.rb +19 -15
  10. data/lib/cheftacular/cloud_provider.rb +32 -0
  11. data/lib/cheftacular/dns.rb +1 -1
  12. data/lib/cheftacular/error.rb +13 -2
  13. data/lib/cheftacular/file_system.rb +122 -0
  14. data/lib/cheftacular/files/rvm.sh +55 -0
  15. data/lib/cheftacular/getter.rb +10 -3
  16. data/lib/cheftacular/helper.rb +31 -127
  17. data/lib/cheftacular/initialization_action.rb +8 -0
  18. data/lib/cheftacular/initializer.rb +104 -44
  19. data/lib/cheftacular/parser.rb +6 -0
  20. data/lib/cheftacular/stateless_actions/add_ssh_key_to_bag.rb +2 -2
  21. data/lib/cheftacular/stateless_actions/arguments.rb +9 -2
  22. data/lib/cheftacular/stateless_actions/backups.rb +1 -1
  23. data/lib/cheftacular/stateless_actions/bootstrappers/ubuntu_bootstrap.rb +16 -5
  24. data/lib/cheftacular/stateless_actions/check_cheftacular_yml_keys.rb +79 -0
  25. data/lib/cheftacular/stateless_actions/chef_bootstrap.rb +20 -4
  26. data/lib/cheftacular/stateless_actions/chef_server.rb +78 -0
  27. data/lib/cheftacular/stateless_actions/clean_cookbooks.rb +3 -3
  28. data/lib/cheftacular/stateless_actions/clean_server_passwords.rb +3 -1
  29. data/lib/cheftacular/stateless_actions/cleanup_log_files.rb +1 -0
  30. data/lib/cheftacular/stateless_actions/client_list.rb +2 -2
  31. data/lib/cheftacular/stateless_actions/cloud.rb +30 -2
  32. data/lib/cheftacular/stateless_actions/cloud_bootstrap.rb +7 -13
  33. data/lib/cheftacular/stateless_actions/compile_audit_log.rb +1 -1
  34. data/lib/cheftacular/stateless_actions/create_git_key.rb +3 -1
  35. data/lib/cheftacular/stateless_actions/environment.rb +2 -0
  36. data/lib/cheftacular/stateless_actions/file.rb +4 -2
  37. data/lib/cheftacular/stateless_actions/fix_known_hosts.rb +2 -1
  38. data/lib/cheftacular/stateless_actions/full_bootstrap.rb +2 -0
  39. data/lib/cheftacular/stateless_actions/get_active_ssh_connections.rb +1 -1
  40. data/lib/cheftacular/stateless_actions/help.rb +9 -7
  41. data/lib/cheftacular/stateless_actions/initialize_cheftacular_yml.rb +29 -0
  42. data/lib/cheftacular/stateless_actions/initialize_data_bag_contents.rb +5 -36
  43. data/lib/cheftacular/stateless_actions/list_toggleable_roles.rb +45 -0
  44. data/lib/cheftacular/stateless_actions/location_aliases.rb +24 -0
  45. data/lib/cheftacular/stateless_actions/pass.rb +4 -4
  46. data/lib/cheftacular/stateless_actions/reinitialize.rb +0 -6
  47. data/lib/cheftacular/stateless_actions/remove_client.rb +12 -7
  48. data/lib/cheftacular/stateless_actions/role_toggle.rb +141 -0
  49. data/lib/cheftacular/stateless_actions/server_update.rb +2 -2
  50. data/lib/cheftacular/stateless_actions/update_chef_client.rb +18 -0
  51. data/lib/cheftacular/stateless_actions/upload_nodes.rb +5 -3
  52. data/lib/cheftacular/stateless_actions/upload_roles.rb +1 -1
  53. data/lib/cheftacular/version.rb +1 -1
  54. data/lib/cloud_interactor/authentication.rb +78 -40
  55. data/lib/cloud_interactor/cloud_interactor.rb +4 -1
  56. data/lib/cloud_interactor/cloud_provider.rb +19 -0
  57. data/lib/cloud_interactor/domain/create.rb +1 -1
  58. data/lib/cloud_interactor/domain/create_record.rb +1 -1
  59. data/lib/cloud_interactor/domain/destroy_record.rb +1 -1
  60. data/lib/cloud_interactor/domain/list_records.rb +1 -1
  61. data/lib/cloud_interactor/domain/update.rb +1 -1
  62. data/lib/cloud_interactor/domain/update_record.rb +6 -6
  63. data/lib/cloud_interactor/helpers.rb +1 -1
  64. data/lib/cloud_interactor/parser.rb +3 -2
  65. data/lib/cloud_interactor/region.rb +27 -0
  66. data/lib/cloud_interactor/server/create.rb +9 -0
  67. data/lib/cloud_interactor/sshkey.rb +59 -0
  68. data/lib/ridley/monkeypatches.rb +12 -0
  69. data/lib/sshkit/actions/start_task.rb +8 -2
  70. metadata +16 -2
@@ -83,7 +83,35 @@ class Cheftacular
83
83
  " 2. `read:FLAVOR SIZE` behaves the same as list unless a flavor size is supplied.",
84
84
 
85
85
  " 1. Standard servers are listed as XGB with no spaces in their size, performance servers are listed as X GB with " +
86
- "a space in their size. If you are about to create a server and are unsure, query flavors first."
86
+ "a space in their size. If you are about to create a server and are unsure, query flavors first.",
87
+
88
+ " 5. `image` first level argument for listing the images available on the cloud service",
89
+
90
+ " 1. `list` default behavior",
91
+
92
+ " 2. `read:NAME` behaves the same as list unless a specific image name is supplied",
93
+
94
+ " 6. `region` first level argument for listing the regions available on the cloud service (only supported by DigitalOcean)",
95
+
96
+ " 1. `list` default behavior",
97
+
98
+ " 2. `read:REGION` behaves the same as list unless a specific region name is supplied",
99
+
100
+ " 7. `sshkey` first level argument for listing the sshkeys added to the cloud service (only supported by DigitalOcean)",
101
+
102
+ " 1. `list` default behavior",
103
+
104
+ " 2. `read:KEY_NAME` behaves the same as list unless a specific sshkey name is supplied",
105
+
106
+ " 3. `\"create:KEY_NAME:KEY_STRING\"` creates an sshkey object. KEY_STRING must contain the entire value of the ssh public key file. " +
107
+ "The command must be enclosed in quotes.",
108
+
109
+ " 4. `destroy:KEY_NAME` destroys the sshkey object",
110
+
111
+ " 5. `bootstrap` captures the current computer's hostname and checks to see if a key matching this hostname exists on the cloud service. " +
112
+ "If the key does not exist, the command attempts to read the contents of the ~/.ssh/id_rsa.pub file and create a new key with that data and the " +
113
+ "hostname of the current computer. Run automatically when creating DigitalOcean servers. It's worth noting that if the computer's key already " +
114
+ "exists on DigitalOcean under a different name, this specific command will fail with a generic error. Please check your keys."
87
115
  ]
88
116
  ]
89
117
  end
@@ -95,7 +123,7 @@ class Cheftacular
95
123
 
96
124
  args = ARGV[1..ARGV.length] if args.empty?
97
125
 
98
- @config['cloud_interactor'] ||= CloudInteractor.new(@config['default']['authentication_bag_hash'], @options)
126
+ @config['cloud_interactor'] ||= CloudInteractor.new(@config['cheftacular'], @options)
99
127
 
100
128
  @config['cloud_interactor'].run args
101
129
  end
@@ -36,26 +36,20 @@ class Cheftacular
36
36
  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)"
37
37
  end
38
38
 
39
+ real_node_name = @config['getter'].get_current_real_node_name
40
+
39
41
  #the output of the cloud command is a hash, this hash is UPDATED every time a rax command is run so you only need to grab it when you need it
40
- @config['stateless_action'].cloud "server", "create:#{ @options['env'] }_#{ @options['node_name'] }:#{ @options['flavor_name'] }"
42
+ @config['stateless_action'].cloud "server", "create:#{ real_node_name }:#{ @options['flavor_name'] }"
41
43
 
42
- status_hash = @config['stateless_action'].cloud "server", "poll:#{ @options['env'] }_#{ @options['node_name'] }"
44
+ status_hash = @config['stateless_action'].cloud "server", "poll:#{ real_node_name }"
43
45
 
44
46
  status_hash['created_servers'].each do |server_hash|
45
- next unless server_hash['name'] == "#{ @options['env'] }_#{ @options['node_name'] }"
46
-
47
- @options['address'] = server_hash['ipv4_address']
47
+ next unless server_hash['name'] == "#{ real_node_name }"
48
48
 
49
- @options['private_address'] = server_hash['addresses']['private'][0]['addr']
49
+ @options['address'], @options['private_address'] = @config['cloud_provider'].parse_addresses_from_server_create_hash server_hash
50
50
  end
51
51
 
52
- begin
53
- @options['client_pass'] = status_hash['admin_passwords']["#{ @options['env'] }_#{ @options['node_name'] }"]
54
- rescue NoMethodError => e
55
- puts "Unable to locate an admin pass for server #{ @options['node_name'] }, does the server already exist? Exiting #{ __method__ }..."
56
-
57
- return false
58
- end
52
+ @options['client_pass'] = @config['cloud_provider'].parse_server_root_password_from_server_create_hash status_hash, real_node_name
59
53
 
60
54
  tld = @config[@options['env']]['config_bag_hash'][@options['sub_env']]['tld']
61
55
 
@@ -32,7 +32,7 @@ class Cheftacular
32
32
 
33
33
  entry_count, int_times = 1, []
34
34
 
35
- compiled_audit_hash[day].keys.each do |time|
35
+ compiled_audit_hash[day].keys.sort.each do |time|
36
36
  out << "#{ entry_count }. #{ time }"
37
37
 
38
38
  log_array_entry_count = 1
@@ -9,7 +9,9 @@ class Cheftacular
9
9
  " 1. This command will upload both the private and public key to the data bag. " +
10
10
  "The public key should be the one that matches the github user for your deployment github user.",
11
11
 
12
- " 2. `OAUTH_TOKEN` *must* be generated by logging into github and generating an access token in the account settings -> applications -> personal access tokens"
12
+ " 2. `OAUTH_TOKEN` *must* be generated by logging into github and generating an access token in the account settings -> applications -> personal access tokens",
13
+
14
+ " 3. NOTE! The ID_RSA_FILE should be in your .chef folder in the root of your home directory!"
13
15
  ]
14
16
  ]
15
17
  end
@@ -73,6 +73,8 @@ class Cheftacular
73
73
 
74
74
  #TODO INTEGRATE backups TO LOAD DATA INTO THE NEWLY BOOTED ENV
75
75
  when 'destroy'
76
+ raise "This action can only be performed if the mode is set to devops" if !@config['helper'].running_in_mode?('devops')
77
+
76
78
  if ask_on_destroy
77
79
  puts "Preparing to delete nodes in #{ @options['env'] }.\nEnter Y/y to confirm."
78
80
 
@@ -126,9 +126,11 @@ class Cheftacular
126
126
  target_loc = "#{ location }/#{ file_name }"
127
127
  puts("Beginning #{ command } run on #{ target_loc } for #{ n.name } (#{ n.public_ipaddress })") unless @options['quiet']
128
128
 
129
- download_location = @options['save_to_file'] ? @options['save_to_file'] : "#{ @config['locs']['chef-log'] }/#{ file_name }"
129
+ download_location = @options['save_to_file'] ? @options['save_to_file'] : "#{ @config['locs']['chef-log'] }/#{ file_name.split('/').last }"
130
130
 
131
131
  `scp -oStrictHostKeyChecking=no #{ @config['cheftacular']['deploy_user'] }@#{ n.public_ipaddress }:#{ location }/#{ file_name } #{ download_location } > /dev/tty`
132
+
133
+ puts "Finished downloading #{ file_name } to #{ download_location }!"
132
134
  end
133
135
  end
134
136
  end
@@ -181,4 +183,4 @@ module SSHKit
181
183
  end
182
184
  end
183
185
  end
184
- end
186
+ end
@@ -42,11 +42,12 @@ class Cheftacular
42
42
  end
43
43
 
44
44
  targets.each do |target|
45
+ puts "clearing #{ target }"
45
46
  case CONFIG['host_os']
46
47
  when /mswin|windows/i
47
48
  raise "#{ __method__ } does not support this operating system at this time"
48
49
  when /linux|arch/i
49
- cleanup_known_hosts_for_BSD_linux_architecture target
50
+ puts "#{ __method__ } does not support this operating system at this time"
50
51
  when /sunos|solaris/i
51
52
  raise "#{ __method__ } does not support this operating system at this time"
52
53
  when /darwin/i
@@ -24,6 +24,8 @@ class Cheftacular
24
24
 
25
25
  @config['initializer'].initialize_passwords @options['env'] #reset the passwords var to contain the new deploy pass set in ubuntu_bootstrap
26
26
 
27
+ @config['helper'].install_rvm_sh_file if @config['cheftacular']['install_rvm_on_boot']
28
+
27
29
  @config['stateless_action'].chef_bootstrap
28
30
  end
29
31
  end
@@ -2,7 +2,7 @@ class Cheftacular
2
2
  class StatelessActionDocumentation
3
3
  def get_active_ssh_connections
4
4
  @config['documentation']['stateless_action'] << [
5
- "`cft get_active_ssh_connections` will fetch the active ssh connections from every server and output it into your log directory."
5
+ "[NYI]`cft get_active_ssh_connections` will fetch the active ssh connections from every server and output it into your log directory."
6
6
  ]
7
7
 
8
8
  @config['documentation']['application'] << @config['documentation']['stateless_action'].last
@@ -11,6 +11,12 @@ class Cheftacular
11
11
  end
12
12
  end
13
13
 
14
+ class InitializationAction
15
+ def help
16
+
17
+ end
18
+ end
19
+
14
20
  class StatelessAction
15
21
  def help inference_modes=[]
16
22
  target_command = @options['command'] == 'help' ? ARGV[1] : ARGV[0]
@@ -45,15 +51,11 @@ class Cheftacular
45
51
 
46
52
  end if inference_modes.include?('stateless_action') || inference_modes.include?('both')
47
53
 
48
- puts @config['helper'].compile_documentation_lines('action').flatten.join("\n\n") if target_command == 'action'
49
-
50
54
  puts @config['documentation']['arguments'].flatten.join("\n\n") if target_command == 'arguments'
51
55
 
52
- puts @config['helper'].compile_documentation_lines('stateless_action').flatten.join("\n\n") if target_command == 'stateless_action'
53
-
54
- puts @config['helper'].compile_documentation_lines('application').flatten.join("\n\n") if target_command == 'application' || target_command.empty?
56
+ puts @config['helper'].compile_documentation_lines('application').flatten.join("\n\n") if target_command.empty?
55
57
 
56
- puts @config['helper'].compile_documentation_lines('devops').flatten.join("\n\n") if target_command == 'devops'
58
+ puts @config['helper'].compile_documentation_lines(target_command).flatten.join("\n\n") if target_command =~ /action|stateless_action|application|devops/
57
59
 
58
60
  if inference_modes.empty? && @config['helper'].is_not_command_or_stateless_command?(target_command)
59
61
  methods = @config['action_documentation'].public_methods(false) + @config['stateless_action_documentation'].public_methods(false)
@@ -64,7 +66,7 @@ class Cheftacular
64
66
  puts " #{ sorted_methods.at(0) }"
65
67
  puts " #{ sorted_methods.at(1) }"
66
68
  puts " #{ sorted_methods.at(2) }\n"
67
- puts "If so, please run 'cft help COMMAND' with one of the above commands or run 'hip help application' to see a list of commands"
69
+ puts "If so, please run 'cft help COMMAND' with one of the above commands or run 'cft help #{ @config['cheftacular']['mode'] }' to see a list of commands"
68
70
  end
69
71
  end
70
72
  end
@@ -0,0 +1,29 @@
1
+ class Cheftacular
2
+ class StatelessActionDocumentation
3
+ def initialize_cheftacular_yml
4
+ @config['documentation']['stateless_action'] << [
5
+ "`cft initialize_cheftacular_yml` will create a cheftacular.yml file in your config folder (and create the" +
6
+ "config folder if it does not exist). If you already have a cheftacular.yml file in the config folder, it will " +
7
+ "create a cheftacular.example.yml file that will contain the new changes / keys in the latest cheftacular version."
8
+ ]
9
+ end
10
+ end
11
+
12
+ class InitializationAction
13
+ def initialize_cheftacular_yml
14
+
15
+ end
16
+ end
17
+
18
+ class StatelessAction
19
+ def initialize_cheftacular_yml
20
+ FileUtils.mkdir_p(File.join(@config['locs']['chef-repo'], "config"))
21
+
22
+ if File.exist?(File.join(@config['locs']['chef-repo'], "config", "cheftacular.yml"))
23
+ @config['helper'].write_config_cheftacular_yml_file('cheftacular.example.yml')
24
+ else
25
+ @config['helper'].write_config_cheftacular_yml_file
26
+ end
27
+ end
28
+ end
29
+ end
@@ -10,7 +10,7 @@ class Cheftacular
10
10
 
11
11
  class StatelessAction
12
12
  def initialize_data_bag_contents env=""
13
- raise "Environment does not exist on chef server!" unless @config['chef_environments'].include?(env)
13
+ raise "Environment #{ env } does not exist on chef server!" if !env.blank? && !@config['chef_environments'].include?(env)
14
14
 
15
15
  env = ARGV[1] if env.blank?
16
16
 
@@ -29,6 +29,8 @@ class Cheftacular
29
29
  #@config['initializer'].initialize_logs_bag_contents env
30
30
 
31
31
  #@config['ChefDataBag'].initialize_node_roles_bag_contents env
32
+
33
+ exit if @options['command'] == __method__
32
34
  end
33
35
  end
34
36
 
@@ -58,48 +60,15 @@ class Cheftacular
58
60
  end
59
61
  end
60
62
 
61
- hash['cloud_auth'] = {} unless hash.has_key?('cloud_auth')
62
-
63
- unless hash['cloud_auth'].has_key?(@options['preferred_cloud'])
64
- hash['cloud_auth'][@options['preferred_cloud']] = {}
65
-
66
- puts "Critical! No Cloud credentials detected for cloud #{ @options['preferred_cloud'] }, " +
67
- "Someone will need to edit the authentication data bag with the command " +
68
- auth_fix_message +
69
- "And add the correct cloud credentials to the hash for your preferred cloud!"
70
-
71
- save_on_finish, exit_on_finish = true,true
72
- end
73
-
74
- if hash['cloud_auth'].has_key?('rackspace')
75
- if !( hash['cloud_auth']['rackspace'].has_key?('username') || hash['cloud_auth']['rackspace'].has_key?('api_key') || hash['cloud_auth']['rackspace'].has_key?('email'))
76
-
77
- hash['cloud_auth']['rackspace']['username'] = "" unless hash['cloud_auth']['rackspace'].has_key?('username')
78
- hash['cloud_auth']['rackspace']['api_key'] = "" unless hash['cloud_auth']['rackspace'].has_key?('api_key')
79
- hash['cloud_auth']['rackspace']['email'] = "" unless hash['cloud_auth']['rackspace'].has_key?('email')
80
-
81
-
82
- puts "Critical! No cloud credentials detected for the rackspace cloud_auth hash! There must be both a valid username and an api_key in this hash!"
83
- puts "Please run #{ auth_fix_message }Fill in the blank strings with your rackspace credentials."
84
-
85
- save_on_finish, exit_on_finish = true,true
86
- elsif hash['cloud_auth']['rackspace']['username'].empty? || hash['cloud_auth']['rackspace']['api_key'].empty? || hash['cloud_auth']['rackspace']['api_key'].empty?
87
- puts "Critical! Cloud credentials detected for the rackspace cloud_auth hash are blank!"
88
- puts "Please run #{ auth_fix_message }Fill in the blank strings with your rackspace credentials."
89
-
90
- exit_on_finish = true
91
- end
92
- end
93
-
94
63
  if @config['cheftacular']['git_based_deploys']
95
64
  if !hash.has_key?('git_private_key') || !hash.has_key?('git_public_key') || !hash.has_key?('git_OAuth')
96
- puts "Warning! github user credentials in default authentication bag were not found! Please run `cft help create_git_key` and then run that command itself!" unless @command == 'help'
65
+ puts "Warning! github user credentials in default authentication bag were not found! Please run `cft help create_git_key` and then run that command itself!" unless @options['command'] == 'help'
97
66
  end
98
67
  end
99
68
 
100
69
  @config['ChefDataBag'].save_authentication_bag if save_on_finish
101
70
 
102
- exit if exit_on_finish
71
+ exit if exit_on_finish && @options['command'] != 'create_git_key'
103
72
  end
104
73
 
105
74
  #User Action Generated: {"1.2.3.4-deploy-pass": "S325DSAGBCVfg5", "1.2.3.4-root-pass": "7dfDSFgb5%231", "1.2.3.4-name": "test"}
@@ -0,0 +1,45 @@
1
+
2
+ class Cheftacular
3
+ class StatelessActionDocumentation
4
+ def list_toggleable_roles
5
+ @config['documentation']['stateless_action'] << [
6
+ "`cft list_toggleable_roles NODE_NAME` This command will allow you to see all toggleable roles for a node"
7
+ ]
8
+
9
+ @config['documentation']['application'] << @config['documentation']['stateless_action'].last
10
+ end
11
+ end
12
+
13
+ class StatelessAction
14
+ def list_toggleable_roles possible_toggles=[]
15
+ @options['node_name'] = ARGV[1] unless @options['node_name']
16
+
17
+ raise "You have yet to fully configure your role toggling settings! Exiting..." if @config['cheftacular']['role_toggling'].has_key?('do_not_allow_toggling')
18
+
19
+ nodes = @config['error'].is_valid_node_name_option?
20
+
21
+ suffix = @config['cheftacular']['role_toggling']['deactivated_role_suffix']
22
+
23
+ nodes.first.run_list.each do |role|
24
+ role = role.gsub('role[','').gsub(']','')
25
+
26
+ if !role.include?(suffix) && @config['parser'].parse_role("#{ role }#{ suffix }", 'boolean')
27
+ possible_toggles << role
28
+ possible_toggles << "#{ role }#{ suffix }"
29
+
30
+ elsif role.include?(suffix) && @config['parser'].parse_role("#{ role.gsub(suffix,'') }", 'boolean')
31
+ possible_toggles << role
32
+ possible_toggles << role.gsub(suffix,'')
33
+ end
34
+ end
35
+
36
+ puts "The current run_list for #{ @options['node_name'] } is:"
37
+
38
+ ap nodes.first.run_list
39
+
40
+ puts "\nThe possible toggles for #{ @options['node_name'] } are:"
41
+
42
+ ap possible_toggles
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,24 @@
1
+ class Cheftacular
2
+ class StatelessActionDocumentation
3
+ def location_aliases
4
+ @config['documentation']['stateless_action'] << [
5
+ "`cft location_aliases` will list all location aliases listed in your cheftacular.yml. These aliases can be used " +
6
+ "in the `cft file` command."
7
+ ]
8
+
9
+ @config['documentation']['application'] << @config['documentation']['stateless_action'].last
10
+ end
11
+ end
12
+
13
+ class InitializationAction
14
+ def location_aliases
15
+
16
+ end
17
+ end
18
+
19
+ class StatelessAction
20
+ def location_aliases
21
+ ap @config['cheftacular']['location_aliases']
22
+ end
23
+ end
24
+ end
@@ -26,17 +26,17 @@ class Cheftacular
26
26
 
27
27
  case CONFIG['host_os']
28
28
  when /mswin|windows/i
29
- raise "#{ __method__ } does not support this operating system at this time"
29
+ #raise "#{ __method__ } does not support this operating system at this time"
30
30
  when /linux|arch/i
31
- raise "#{ __method__ } does not support this operating system at this time"
31
+ #raise "#{ __method__ } does not support this operating system at this time"
32
32
  when /sunos|solaris/i
33
- raise "#{ __method__ } does not support this operating system at this time"
33
+ #raise "#{ __method__ } does not support this operating system at this time"
34
34
  when /darwin/i
35
35
  puts "Copying #{ nodes.first.name } (#{ nodes.first.public_ipaddress }) sudo password into your clipboard"
36
36
 
37
37
  `echo '#{ @config[nodes.first.chef_environment]['server_passwords_bag_hash']["#{ nodes.first.public_ipaddress }-deploy-pass"] }' | pbcopy`
38
38
  else
39
- raise "#{ __method__ } does not support this operating system at this time"
39
+ #raise "#{ __method__ } does not support this operating system at this time"
40
40
  end
41
41
  end
42
42
  end
@@ -30,17 +30,11 @@ class Cheftacular
30
30
 
31
31
  out << `ssh -t -oStrictHostKeyChecking=no #{ chef_user }@#{ @options['address'] } "#{ sudo(@options['address']) } rm /etc/chef/client.pem"`
32
32
 
33
- #remove_client #just in case
34
-
35
33
  puts("Starting reinitialization...") unless @options['quiet']
36
34
 
37
35
  out << `#{ @config['helper'].knife_bootstrap_command }`
38
36
 
39
37
  puts(out.last) unless @options['quiet']
40
-
41
- #@options['multi-step'] = true # have the upload_nodes grab the new nodes
42
-
43
- #upload_nodes
44
38
  end
45
39
  end
46
40
  end
@@ -3,9 +3,14 @@ class Cheftacular
3
3
  class StatelessActionDocumentation
4
4
  def remove_client
5
5
  @config['documentation']['stateless_action'] << [
6
- "`cft remove_client -n NODE_NAME` removes a client (and its node data) from the chef-server. " +
6
+ "`cft remove_client NODE_NAME [destroy]` removes a client (and its node data) from the chef-server. " +
7
7
  "It also removes its dns records from the cloud service (if possible). " +
8
- "This should not be done lightly as you will have to wipe the server and trigger another chef-client run to get it to register again"
8
+ "This should not be done lightly as you will have to wipe the server and trigger another chef-client " +
9
+ "run to get it to register again. Alternatively, you can run `cft reinitialize IP_ADDRESS NODE_NAME as well.",
10
+
11
+ [
12
+ " 1. `destroy` deletes the server as well as removing it from the chef environment."
13
+ ]
9
14
  ]
10
15
  end
11
16
  end
@@ -47,15 +52,15 @@ class Cheftacular
47
52
  @config['ridley'].client.delete(client)
48
53
 
49
54
  if @options['delete_server_on_remove'] == 'destroy'
50
- @config['stateless_action'].cloud "server", "destroy:#{ @options['env'] }_#{ n.name }"
55
+ @config['stateless_action'].cloud "server", "destroy:#{ @config['getter'].get_current_real_node_name(n.name) }"
51
56
  end
52
- end
53
57
 
54
- @config[@options['env']]['addresses_bag_hash'] = @config[@options['env']]['addresses_bag'].reload.to_hash
58
+ @config[@options['env']]['addresses_bag_hash'] = @config[@options['env']]['addresses_bag'].reload.to_hash
55
59
 
56
- @config['DNS'].compile_address_hash_for_server_from_options('set_hash_to_nil')
60
+ @config['DNS'].compile_address_hash_for_server_from_options('set_hash_to_nil')
57
61
 
58
- @config['ChefDataBag'].save_addresses_bag
62
+ @config['ChefDataBag'].save_addresses_bag
63
+ end
59
64
  end
60
65
 
61
66
  puts("Done. Please verify that the output of the next line(s) match your expectations (running client-list)") if @options['verbose']
@@ -0,0 +1,141 @@
1
+
2
+ class Cheftacular
3
+ class StatelessActionDocumentation
4
+ def role_toggle
5
+ @config['documentation']['stateless_action'] << [
6
+ "`cft role_toggle NODE_NAME ROLE_NAME activate|deactivate` This command will allow you to **toggle** roles on nodes without using `cft upload_nodes`",
7
+
8
+ [
9
+ " 1. This command uses your *role_toggling:deactivated_role_suffix* attribute set in your cheftacular.yml to toggle the role, " +
10
+ "it checks to see if the toggled name exists then sets the node's run_list to include the toggled role",
11
+
12
+ " 2. EX: `cft role_toggle apisc01 worker activate` will find the node apisc01 and attempt to toggle the worker role to on. " +
13
+ "If the node does NOT have the worker#{ @config['cheftacular']['role_toggling']['deactivated_role_suffix'] } role, then it will " +
14
+ "add it if *role_toggling:strict_roles* is set to **false**",
15
+
16
+ " 1. If *role_toggling:strict_roles* is set to true, then cheftacular would raise an error saying this role is unsettable " +
17
+ "on the node. On the other hand, if the node already has the worker#{ @config['cheftacular']['role_toggling']['deactivated_role_suffix'] }" +
18
+ "role, then this command will succeed even if *strict_roles* is set.",
19
+
20
+ " 3. In case it isn't obvious, this command ONLY supports deactivation suffix roles like worker_deactivate or worker_off, with their" +
21
+ "on counterpart just being the role itself, like \"worker\".",
22
+
23
+ " 1. Please run `cft list_toggleable_roles NODE_NAME` to get a list of your org's toggleable roles for a node."
24
+ ]
25
+ ]
26
+
27
+ @config['documentation']['application'] << @config['documentation']['stateless_action'].last
28
+ end
29
+ end
30
+
31
+ class StatelessAction
32
+ def role_toggle state_toggle='', target_run_list=[], skip_confirm=false
33
+ @options['node_name'] = ARGV[1] unless @options['node_name']
34
+ @config['parser'].parse_role(ARGV[2])
35
+ state_toggle = ARGV[3] if state_toggle.blank?
36
+
37
+ raise "You have yet to fully configure your role toggling settings! Exiting..." if @config['cheftacular']['role_toggling'].has_key?('do_not_allow_toggling')
38
+ raise "You may only enter activate or deactivate for the state toggle argument for the #{ __method__ } command." unless (state_toggle =~ /activate|deactivate/) == 0
39
+
40
+ @config['initializer'].initialize_data_bags_for_environment @options['env'], false, ['node_roles']
41
+
42
+ @config['initializer'].initialize_node_roles_bag_contents @options['env']
43
+
44
+ @config['filesystem'].cleanup_file_caches('current')
45
+
46
+ nodes = @config['error'].is_valid_node_name_option?
47
+
48
+ suffix = @config['cheftacular']['role_toggling']['deactivated_role_suffix']
49
+
50
+ if @options['role'].include?(suffix)
51
+ unless @config['parser'].parse_role("#{ @options['role'].gsub(suffix,'') }", 'boolean')
52
+ puts "Role #{ @options['role'] } does not have an activated role! There is no #{ @options['role'].gsub(suffix,'') } role!"
53
+
54
+ return false
55
+ end
56
+ else
57
+ unless @config['parser'].parse_role("#{ @options['role'] }#{ suffix }", 'boolean')
58
+ puts "Role #{ @options['role'] } does not have a deactivated role! There is no #{ @options['role'] }#{ suffix } role!"
59
+
60
+ return false
61
+ end
62
+ end
63
+
64
+ current_node_roles = nodes.first.run_list
65
+
66
+ if current_node_roles.include?("role[#{ @options['role'] }]") && !@options['role'].include?(suffix)
67
+ if state_toggle == 'activate'
68
+ puts "The role #{ @options['role'] } is already activated for #{ nodes.first.name }!"
69
+ else
70
+ puts "The role #{ @options['role'] } is currently activated, setting it to #{ @options['role'] }#{ suffix }"
71
+
72
+ target_run_list = current_node_roles.map {|r| r.gsub(@options['role'], "#{ @options['role'] }#{ suffix }") if current_node_roles.include?("role[#{ @options['role'] }]") }
73
+ end
74
+ elsif current_node_roles.include?("role[#{ @options['role'] }]") && @options['role'].include?(suffix)
75
+ if state_toggle == 'activate'
76
+ puts "The role #{ @options['role'] } is currently deactivated, setting it to #{ @options['role'].gsub(suffix, '') }"
77
+
78
+ target_run_list = current_node_roles.map {|r| r.gsub(@options['role'], "#{ @options['role'].gsub(suffix, '') }") if current_node_roles.include?("role[#{ @options['role'] }]") }
79
+ else
80
+ puts "The role #{ @options['role'] } is already deactivated for #{ nodes.first.name }!"
81
+ end
82
+ elsif current_node_roles.include?("role[#{ @options['role'] }#{ suffix }]") #they passed in the reverse of a role that was already deactivated
83
+ if state_toggle == 'activate'
84
+ puts "The role #{ @options['role'] } is currently deactivated, setting it to it's activated state"
85
+
86
+ target_run_list = current_node_roles.map {|r| r.gsub("#{ @options['role'] }#{ suffix }", "#{ @options['role'] }") if current_node_roles.include?("role[#{ @options['role'] }#{ suffix }]") }
87
+ else
88
+ puts "The role #{ @options['role'] } is already deactivated for #{ nodes.first.name }!"
89
+ end
90
+ elsif current_node_roles.include?("role[#{ @options['role'].gsub(suffix, '') }]") #they passed in the reverse of a role that was already activated
91
+ if state_toggle == 'activate'
92
+ puts "The role #{ @options['role'] } is already activated for #{ nodes.first.name }!"
93
+ else
94
+ puts "The role #{ @options['role'] } is currently activated, setting it to it's deactivated state"
95
+
96
+ target_run_list = current_node_roles.map {|r| r.gsub("#{ @options['role'].gsub('suffix','') }", "#{ @options['role'] }") if current_node_roles.include?("role[#{ @options['role'].gsub('suffix','') }]") }
97
+ end
98
+ elsif !current_node_roles.include?("role[#{ @options['role'] }]") && @config['cheftacular']['role_toggling']['strict_roles']
99
+ puts "The node does not have #{ @options['role'] } and strict roles is set to true, exiting..."
100
+
101
+ return false
102
+ elsif !current_node_roles.include?("role[#{ @options['role'] }]") && !@config['cheftacular']['role_toggling']['strict_roles']
103
+ puts "The node does not have #{ @options['role'] } and strict roles is set to false, setting this new role..."
104
+
105
+ target_run_list = current_node_roles + "role[#{ @options['role'] }"
106
+ end
107
+
108
+ unless target_run_list.empty?
109
+ puts "Updating node run list for #{ nodes.first.name } from"
110
+
111
+ ap current_node_roles
112
+
113
+ puts "to:"
114
+
115
+ ap target_run_list
116
+
117
+ if skip_confirm || !@config['cheftacular']['role_toggling']['skip_confirm']
118
+ puts "Enter Y/y to confirm."
119
+
120
+ input = STDIN.gets.chomp
121
+
122
+ return false unless ( input =~ /y|Y|yes|Yes/ ) == 0
123
+ end
124
+
125
+ @config[@options['env']]['node_roles_bag_hash']['node_roles'][nodes.first.name.gsub(/\d/,'')]['run_list'] = target_run_list
126
+
127
+ nodes.first.send("run_list=", target_run_list)
128
+
129
+ nodes.first.save
130
+
131
+ @config['ChefDataBag'].save_node_roles_bag @options['env']
132
+
133
+ @config['filesystem'].cleanup_file_caches('current')
134
+
135
+ puts "Triggering deploy to set the new role..."
136
+
137
+ @config['action'].deploy
138
+ end
139
+ end
140
+ end
141
+ end
@@ -7,7 +7,7 @@ class Cheftacular
7
7
  "This should be done with caution as this *might* break something.",
8
8
 
9
9
  [
10
- " 1. `hip apt_update restart` will prompt to ask if you also want to restart all servers in a rolling restart. " +
10
+ " 1. `cft apt_update restart` will prompt to ask if you also want to restart all servers in a rolling restart. " +
11
11
  "This should be done with extreme caution and only in a worst-case scenario."
12
12
  ]
13
13
  ]
@@ -96,4 +96,4 @@ module SSHKit
96
96
  end
97
97
  end
98
98
  end
99
- end
99
+ end
@@ -0,0 +1,18 @@
1
+
2
+ class Cheftacular
3
+ class StatelessActionDocumentation
4
+ def update_chef_client
5
+ @config['documentation']['stateless_action'] << [
6
+ "[NYI]`cft update_chef_client` attempts to update the chef-client of all nodes to the latest version. " +
7
+ "Should be done with caution and with the chef_server's version in mind."
8
+ ]
9
+ end
10
+ end
11
+
12
+ class StatelessAction
13
+ def update_chef_client
14
+ raise "Not Yet Implemented"
15
+ raise "This action can only be performed if the mode is set to devops" unless @config['helper'].running_in_mode?('devops')
16
+ end
17
+ end
18
+ end