cheftacular 2.5.0 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|