cheftacular 2.5.0 → 2.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/cheftacular/README.md +155 -63
- data/lib/cheftacular/actions/db_console.rb +3 -3
- data/lib/cheftacular/actions/deploy.rb +18 -12
- data/lib/cheftacular/actions/migrate.rb +1 -1
- data/lib/cheftacular/actions/tail.rb +1 -1
- data/lib/cheftacular/auditor.rb +14 -6
- data/lib/cheftacular/chef/data_bag.rb +1 -4
- data/lib/cheftacular/cheftacular.rb +19 -15
- data/lib/cheftacular/cloud_provider.rb +32 -0
- data/lib/cheftacular/dns.rb +1 -1
- data/lib/cheftacular/error.rb +13 -2
- data/lib/cheftacular/file_system.rb +122 -0
- data/lib/cheftacular/files/rvm.sh +55 -0
- data/lib/cheftacular/getter.rb +10 -3
- data/lib/cheftacular/helper.rb +31 -127
- data/lib/cheftacular/initialization_action.rb +8 -0
- data/lib/cheftacular/initializer.rb +104 -44
- data/lib/cheftacular/parser.rb +6 -0
- data/lib/cheftacular/stateless_actions/add_ssh_key_to_bag.rb +2 -2
- data/lib/cheftacular/stateless_actions/arguments.rb +9 -2
- data/lib/cheftacular/stateless_actions/backups.rb +1 -1
- data/lib/cheftacular/stateless_actions/bootstrappers/ubuntu_bootstrap.rb +16 -5
- data/lib/cheftacular/stateless_actions/check_cheftacular_yml_keys.rb +79 -0
- data/lib/cheftacular/stateless_actions/chef_bootstrap.rb +20 -4
- data/lib/cheftacular/stateless_actions/chef_server.rb +78 -0
- data/lib/cheftacular/stateless_actions/clean_cookbooks.rb +3 -3
- data/lib/cheftacular/stateless_actions/clean_server_passwords.rb +3 -1
- data/lib/cheftacular/stateless_actions/cleanup_log_files.rb +1 -0
- data/lib/cheftacular/stateless_actions/client_list.rb +2 -2
- data/lib/cheftacular/stateless_actions/cloud.rb +30 -2
- data/lib/cheftacular/stateless_actions/cloud_bootstrap.rb +7 -13
- data/lib/cheftacular/stateless_actions/compile_audit_log.rb +1 -1
- data/lib/cheftacular/stateless_actions/create_git_key.rb +3 -1
- data/lib/cheftacular/stateless_actions/environment.rb +2 -0
- data/lib/cheftacular/stateless_actions/file.rb +4 -2
- data/lib/cheftacular/stateless_actions/fix_known_hosts.rb +2 -1
- data/lib/cheftacular/stateless_actions/full_bootstrap.rb +2 -0
- data/lib/cheftacular/stateless_actions/get_active_ssh_connections.rb +1 -1
- data/lib/cheftacular/stateless_actions/help.rb +9 -7
- data/lib/cheftacular/stateless_actions/initialize_cheftacular_yml.rb +29 -0
- data/lib/cheftacular/stateless_actions/initialize_data_bag_contents.rb +5 -36
- data/lib/cheftacular/stateless_actions/list_toggleable_roles.rb +45 -0
- data/lib/cheftacular/stateless_actions/location_aliases.rb +24 -0
- data/lib/cheftacular/stateless_actions/pass.rb +4 -4
- data/lib/cheftacular/stateless_actions/reinitialize.rb +0 -6
- data/lib/cheftacular/stateless_actions/remove_client.rb +12 -7
- data/lib/cheftacular/stateless_actions/role_toggle.rb +141 -0
- data/lib/cheftacular/stateless_actions/server_update.rb +2 -2
- data/lib/cheftacular/stateless_actions/update_chef_client.rb +18 -0
- data/lib/cheftacular/stateless_actions/upload_nodes.rb +5 -3
- data/lib/cheftacular/stateless_actions/upload_roles.rb +1 -1
- data/lib/cheftacular/version.rb +1 -1
- data/lib/cloud_interactor/authentication.rb +78 -40
- data/lib/cloud_interactor/cloud_interactor.rb +4 -1
- data/lib/cloud_interactor/cloud_provider.rb +19 -0
- data/lib/cloud_interactor/domain/create.rb +1 -1
- data/lib/cloud_interactor/domain/create_record.rb +1 -1
- data/lib/cloud_interactor/domain/destroy_record.rb +1 -1
- data/lib/cloud_interactor/domain/list_records.rb +1 -1
- data/lib/cloud_interactor/domain/update.rb +1 -1
- data/lib/cloud_interactor/domain/update_record.rb +6 -6
- data/lib/cloud_interactor/helpers.rb +1 -1
- data/lib/cloud_interactor/parser.rb +3 -2
- data/lib/cloud_interactor/region.rb +27 -0
- data/lib/cloud_interactor/server/create.rb +9 -0
- data/lib/cloud_interactor/sshkey.rb +59 -0
- data/lib/ridley/monkeypatches.rb +12 -0
- data/lib/sshkit/actions/start_task.rb +8 -2
- 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['
|
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:#{
|
42
|
+
@config['stateless_action'].cloud "server", "create:#{ real_node_name }:#{ @options['flavor_name'] }"
|
41
43
|
|
42
|
-
status_hash = @config['stateless_action'].cloud "server", "poll:#{
|
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'] == "#{
|
46
|
-
|
47
|
-
@options['address'] = server_hash['ipv4_address']
|
47
|
+
next unless server_hash['name'] == "#{ real_node_name }"
|
48
48
|
|
49
|
-
@options['
|
49
|
+
@options['address'], @options['private_address'] = @config['cloud_provider'].parse_addresses_from_server_create_hash server_hash
|
50
50
|
end
|
51
51
|
|
52
|
-
|
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
|
|
@@ -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
|
-
|
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('
|
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(
|
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 '
|
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!"
|
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
|
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
|
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:#{ @
|
55
|
+
@config['stateless_action'].cloud "server", "destroy:#{ @config['getter'].get_current_real_node_name(n.name) }"
|
51
56
|
end
|
52
|
-
end
|
53
57
|
|
54
|
-
|
58
|
+
@config[@options['env']]['addresses_bag_hash'] = @config[@options['env']]['addresses_bag'].reload.to_hash
|
55
59
|
|
56
|
-
|
60
|
+
@config['DNS'].compile_address_hash_for_server_from_options('set_hash_to_nil')
|
57
61
|
|
58
|
-
|
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. `
|
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
|