cheftacular 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/cft +4 -0
- data/bin/cftclr +4 -0
- data/bin/cheftacular +4 -0
- data/bin/client-list +4 -0
- data/lib/cheftacular/README.md +416 -0
- data/lib/cheftacular/actions/check.rb +32 -0
- data/lib/cheftacular/actions/console.rb +62 -0
- data/lib/cheftacular/actions/database.rb +13 -0
- data/lib/cheftacular/actions/db_console.rb +67 -0
- data/lib/cheftacular/actions/deploy.rb +40 -0
- data/lib/cheftacular/actions/log.rb +47 -0
- data/lib/cheftacular/actions/migrate.rb +57 -0
- data/lib/cheftacular/actions/run.rb +64 -0
- data/lib/cheftacular/actions/scale.rb +94 -0
- data/lib/cheftacular/actions/tail.rb +55 -0
- data/lib/cheftacular/actions.rb +14 -0
- data/lib/cheftacular/auditor.rb +46 -0
- data/lib/cheftacular/chef/data_bag.rb +104 -0
- data/lib/cheftacular/cheftacular.rb +55 -0
- data/lib/cheftacular/decryptors.rb +45 -0
- data/lib/cheftacular/encryptors.rb +48 -0
- data/lib/cheftacular/getters.rb +153 -0
- data/lib/cheftacular/helpers.rb +296 -0
- data/lib/cheftacular/initializers.rb +451 -0
- data/lib/cheftacular/parsers.rb +199 -0
- data/lib/cheftacular/remote_helpers.rb +30 -0
- data/lib/cheftacular/stateless_action.rb +16 -0
- data/lib/cheftacular/stateless_actions/add_ssh_key_to_bag.rb +44 -0
- data/lib/cheftacular/stateless_actions/arguments.rb +68 -0
- data/lib/cheftacular/stateless_actions/backups.rb +116 -0
- data/lib/cheftacular/stateless_actions/bootstrappers/centos_bootstrap.rb +7 -0
- data/lib/cheftacular/stateless_actions/bootstrappers/coreos_bootstrap.rb +7 -0
- data/lib/cheftacular/stateless_actions/bootstrappers/fedora_bootstrap.rb +7 -0
- data/lib/cheftacular/stateless_actions/bootstrappers/redhat_bootstrap.rb +7 -0
- data/lib/cheftacular/stateless_actions/bootstrappers/ubuntu_bootstrap.rb +102 -0
- data/lib/cheftacular/stateless_actions/bootstrappers/vyatta_bootstrap.rb +7 -0
- data/lib/cheftacular/stateless_actions/chef_bootstrap.rb +40 -0
- data/lib/cheftacular/stateless_actions/chef_environment.rb +21 -0
- data/lib/cheftacular/stateless_actions/clean_cookbooks.rb +104 -0
- data/lib/cheftacular/stateless_actions/clean_sensu_plugins.rb +19 -0
- data/lib/cheftacular/stateless_actions/clean_server_passwords.rb +14 -0
- data/lib/cheftacular/stateless_actions/cleanup_log_files.rb +14 -0
- data/lib/cheftacular/stateless_actions/client_list.rb +89 -0
- data/lib/cheftacular/stateless_actions/cloud.rb +107 -0
- data/lib/cheftacular/stateless_actions/cloud_bootstrap.rb +109 -0
- data/lib/cheftacular/stateless_actions/compile_audit_log.rb +60 -0
- data/lib/cheftacular/stateless_actions/compile_readme.rb +41 -0
- data/lib/cheftacular/stateless_actions/create_git_key.rb +67 -0
- data/lib/cheftacular/stateless_actions/disk_report.rb +75 -0
- data/lib/cheftacular/stateless_actions/environment.rb +100 -0
- data/lib/cheftacular/stateless_actions/fetch_file.rb +24 -0
- data/lib/cheftacular/stateless_actions/fix_known_hosts.rb +70 -0
- data/lib/cheftacular/stateless_actions/full_bootstrap.rb +30 -0
- data/lib/cheftacular/stateless_actions/get_active_ssh_connections.rb +18 -0
- data/lib/cheftacular/stateless_actions/get_haproxy_log.rb +55 -0
- data/lib/cheftacular/stateless_actions/get_log_from_bag.rb +38 -0
- data/lib/cheftacular/stateless_actions/get_pg_pass.rb +61 -0
- data/lib/cheftacular/stateless_actions/help.rb +71 -0
- data/lib/cheftacular/stateless_actions/initialize_data_bag_contents.rb +220 -0
- data/lib/cheftacular/stateless_actions/knife_upload.rb +23 -0
- data/lib/cheftacular/stateless_actions/pass.rb +49 -0
- data/lib/cheftacular/stateless_actions/reinitialize.rb +46 -0
- data/lib/cheftacular/stateless_actions/remove_client.rb +81 -0
- data/lib/cheftacular/stateless_actions/replication_status.rb +103 -0
- data/lib/cheftacular/stateless_actions/restart_swap.rb +55 -0
- data/lib/cheftacular/stateless_actions/rvm.rb +14 -0
- data/lib/cheftacular/stateless_actions/server_update.rb +99 -0
- data/lib/cheftacular/stateless_actions/service.rb +14 -0
- data/lib/cheftacular/stateless_actions/test_env.rb +82 -0
- data/lib/cheftacular/stateless_actions/update_split_branches.rb +64 -0
- data/lib/cheftacular/stateless_actions/update_tld.rb +62 -0
- data/lib/cheftacular/stateless_actions/upload_nodes.rb +120 -0
- data/lib/cheftacular/stateless_actions/upload_roles.rb +24 -0
- data/lib/cheftacular/version.rb +5 -0
- data/lib/cheftacular.rb +4 -0
- data/lib/cloud_interactor/authentication.rb +56 -0
- data/lib/cloud_interactor/cloud_interactor.rb +23 -0
- data/lib/cloud_interactor/domain/create.rb +17 -0
- data/lib/cloud_interactor/domain/create_record.rb +27 -0
- data/lib/cloud_interactor/domain/destroy.rb +17 -0
- data/lib/cloud_interactor/domain/destroy_record.rb +23 -0
- data/lib/cloud_interactor/domain/list.rb +9 -0
- data/lib/cloud_interactor/domain/list_records.rb +22 -0
- data/lib/cloud_interactor/domain/read.rb +23 -0
- data/lib/cloud_interactor/domain/read_record.rb +27 -0
- data/lib/cloud_interactor/domain/update.rb +18 -0
- data/lib/cloud_interactor/domain/update_record.rb +42 -0
- data/lib/cloud_interactor/domain.rb +18 -0
- data/lib/cloud_interactor/flavor.rb +27 -0
- data/lib/cloud_interactor/helpers.rb +70 -0
- data/lib/cloud_interactor/image.rb +27 -0
- data/lib/cloud_interactor/parser.rb +37 -0
- data/lib/cloud_interactor/server/attach_volume.rb +33 -0
- data/lib/cloud_interactor/server/create.rb +39 -0
- data/lib/cloud_interactor/server/destroy.rb +11 -0
- data/lib/cloud_interactor/server/detach_volume.rb +21 -0
- data/lib/cloud_interactor/server/list.rb +7 -0
- data/lib/cloud_interactor/server/list_volumes.rb +25 -0
- data/lib/cloud_interactor/server/poll.rb +22 -0
- data/lib/cloud_interactor/server/read.rb +9 -0
- data/lib/cloud_interactor/server/read_volume.rb +24 -0
- data/lib/cloud_interactor/server.rb +17 -0
- data/lib/cloud_interactor/version.rb +4 -0
- data/lib/cloud_interactor/volume/create.rb +13 -0
- data/lib/cloud_interactor/volume/destroy.rb +11 -0
- data/lib/cloud_interactor/volume/list.rb +7 -0
- data/lib/cloud_interactor/volume/read.rb +9 -0
- data/lib/cloud_interactor/volume.rb +20 -0
- data/lib/ridley/monkeypatches.rb +11 -0
- data/lib/sshkit/actions/start_commit_check.rb +19 -0
- data/lib/sshkit/actions/start_deploy.rb +25 -0
- data/lib/sshkit/actions/start_log_fetch.rb +91 -0
- data/lib/sshkit/actions/start_task.rb +29 -0
- data/lib/sshkit/getters.rb +67 -0
- data/lib/sshkit/helpers.rb +13 -0
- data/lib/sshkit/monkeypatches.rb +19 -0
- metadata +375 -0
@@ -0,0 +1,46 @@
|
|
1
|
+
#This command is meant to be run on servers that are not attached the the current chef server
|
2
|
+
class Cheftacular
|
3
|
+
class StatelessActionDocumentation
|
4
|
+
def reinitialize
|
5
|
+
@config['documentation']['action'] << [
|
6
|
+
"`cft reinitialize IP_ADDRESS NODE_NAME` will reconnect a server previously managed by chef to a new chef server. " +
|
7
|
+
"The node name MUST MATCH THE NODE'S ORIGINAL NODE NAME for the roles to be setup correctly."
|
8
|
+
]
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class StatelessAction
|
13
|
+
def reinitialize out=[]
|
14
|
+
raise "This action can only be performed if the mode is set to devops" unless @config['helper'].running_in_mode?('devops')
|
15
|
+
|
16
|
+
@options['address'] = ARGV[1] unless @options['address']
|
17
|
+
@options['node_name'] = ARGV[2] unless @options['node_name']
|
18
|
+
|
19
|
+
puts("Sending up validator file...") unless @options['quiet']
|
20
|
+
|
21
|
+
chef_user = @config['cheftacular']['deploy_user']
|
22
|
+
|
23
|
+
out << `scp -oStrictHostKeyChecking=no #{ @config['locs']['chef'] }/chef-validator.pem #{ chef_user }@#{ @options['address'] }:/home/#{ chef_user }`
|
24
|
+
|
25
|
+
puts("Moving validator file to chef directory on server...") unless @options['quiet']
|
26
|
+
|
27
|
+
out << `ssh -t -oStrictHostKeyChecking=no #{ chef_user }@#{ @options['address'] } "#{ sudo(@options['address']) } mv -f /home/#{ chef_user }/chef-validator.pem /etc/chef/validator.pem"`
|
28
|
+
|
29
|
+
puts("Removing original client.pem file from server...") unless @options['quiet']
|
30
|
+
|
31
|
+
out << `ssh -t -oStrictHostKeyChecking=no #{ chef_user }@#{ @options['address'] } "#{ sudo(@options['address']) } rm /etc/chef/client.pem"`
|
32
|
+
|
33
|
+
#remove_client #just in case
|
34
|
+
|
35
|
+
puts("Starting reinitialization...") unless @options['quiet']
|
36
|
+
|
37
|
+
out << `#{ @config['helper'].knife_bootstrap_command }`
|
38
|
+
|
39
|
+
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
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
|
2
|
+
class Cheftacular
|
3
|
+
class StatelessActionDocumentation
|
4
|
+
def remove_client
|
5
|
+
@config['documentation']['action'] << [
|
6
|
+
"`cft remove_client -n NODE_NAME` removes a client (and its node data) from the chef-server. " +
|
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"
|
9
|
+
]
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class StatelessAction
|
14
|
+
def remove_client delete_server=false, remove=true
|
15
|
+
@options['node_name'] = ARGV[1] unless @options['node_name']
|
16
|
+
@options['delete_server_on_remove'] = ARGV[2] if !@options['delete_server_on_remove'] && !@options['dont_remove_address_or_server'] && ARGV[2]
|
17
|
+
@options['delete_server_on_remove'] = 'destroy' if delete_server || @options['delete_server_on_remove']
|
18
|
+
|
19
|
+
raise "The only valid argument for the 2nd argument of this command is 'destroy', please enter this or leave it blank." if ARGV[2] && ARGV[2] != 'destroy' && !@options['dont_remove_address_or_server']
|
20
|
+
|
21
|
+
raise "CannotRemoveMultipleClients, Please call this script as hip remove_client <node_name>" unless @options['node_name']
|
22
|
+
|
23
|
+
nodes = @config['getter'].get_true_node_objects(false, true)
|
24
|
+
|
25
|
+
nodes.each do |n|
|
26
|
+
begin
|
27
|
+
client = @config['ridley'].client.find(@options['node_name'])
|
28
|
+
rescue StandardError => e
|
29
|
+
puts "Client #{ @options['node_name'] } not found."
|
30
|
+
return false
|
31
|
+
end
|
32
|
+
|
33
|
+
if @options['env'] == 'production' && !@options['force_yes']
|
34
|
+
puts "Preparing to delete #{ @options['node_name'] } (#{ n.public_ipaddress }).\nEnter Y/y to confirm."
|
35
|
+
|
36
|
+
input = STDIN.gets.chomp
|
37
|
+
|
38
|
+
remove = false unless ( input =~ /y|Y|yes|Yes/ ) == 0
|
39
|
+
end
|
40
|
+
|
41
|
+
if remove
|
42
|
+
puts "Removing #{ n.name } (#{ n.public_ipaddress }) from node and client list"
|
43
|
+
|
44
|
+
@config['ridley'].node.delete(n)
|
45
|
+
@config['ridley'].client.delete(client)
|
46
|
+
|
47
|
+
if @options['delete_server_on_remove'] == 'destroy'
|
48
|
+
@config['stateless_action'].cloud "server", "destroy:#{ @options['env'] }_#{ n.name }"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
@config[@options['env']]['addresses_bag_hash'] = @config[@options['env']]['addresses_bag'].reload.to_hash
|
53
|
+
|
54
|
+
target_serv_index = nil
|
55
|
+
|
56
|
+
@config[@options['env']]['addresses_bag_hash']['addresses'].each do |serv_hash|
|
57
|
+
puts "TARGET::#{ serv_hash['name'] }::#{ @options['node_name'] }::#{ @config[@options['env']]['addresses_bag_hash']['addresses'].index(serv_hash) }"
|
58
|
+
target_serv_index = @config[@options['env']]['addresses_bag_hash']['addresses'].index(serv_hash) if serv_hash['name'] == @options['node_name']
|
59
|
+
end
|
60
|
+
|
61
|
+
if !target_serv_index.nil? && target_serv_index.is_a?(Fixnum) && !@options['dont_remove_address_or_server']
|
62
|
+
puts("Found entry in addresses data bag corresponding to #{ @options['node_name'] } for #{ @options['env'] }, removing...") unless @options['quiet']
|
63
|
+
|
64
|
+
domain_obj = PublicSuffix.parse @config[@options['env']]['addresses_bag_hash']['addresses'][target_serv_index]['dn']
|
65
|
+
|
66
|
+
#delete the domain on rax if its a domain we host there for its environment
|
67
|
+
@config['stateless_action'].cloud "domain", "destroy_record:#{ domain_obj.tld }:#{ domain_obj.trd }" if domain_obj.tld == get_tld_from_node_name
|
68
|
+
|
69
|
+
@config[@options['env']]['addresses_bag_hash']['addresses'][target_serv_index] = nil
|
70
|
+
@config[@options['env']]['addresses_bag_hash']['addresses'] = @config[@options['env']]['addresses_bag_hash']['addresses'].compact
|
71
|
+
end
|
72
|
+
|
73
|
+
@config['ChefDataBag'].save_addresses_bag
|
74
|
+
end
|
75
|
+
|
76
|
+
puts("Done. Please verify that the output of the next line(s) match your expectations (running client-list)") if @options['verbose']
|
77
|
+
|
78
|
+
puts(`client-list`) if @options['verbose']
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
|
2
|
+
class Cheftacular
|
3
|
+
class StatelessActionDocumentation
|
4
|
+
def replication_status
|
5
|
+
@config['documentation']['stateless_action'] << [
|
6
|
+
"`cft replication_status` will check the status of the database master and slaves in every environment. " +
|
7
|
+
"Also lists how far behind the slaves are from the master in milliseconds."
|
8
|
+
]
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class StatelessAction
|
13
|
+
def replication_status rep_status_hash={}, out=[]
|
14
|
+
|
15
|
+
nodes = @config['getter'].get_true_node_objects(true)
|
16
|
+
|
17
|
+
primary_nodes = @config['parser'].exclude_nodes( nodes, [{ if: { env: '_default' }}, { unless: "role[db_primary]"}] )
|
18
|
+
|
19
|
+
slave_nodes = @config['parser'].exclude_nodes( nodes, [{ if: { env: '_default' }}, { unless: "role[db_slave]"}] )
|
20
|
+
|
21
|
+
(primary_nodes + slave_nodes).map {|n| n.chef_environment}.uniq.each do |env|
|
22
|
+
@config['initializer'].initialize_data_bags_for_environment env, false, ['server_passwords']
|
23
|
+
|
24
|
+
@config['initializer'].initialize_passwords env
|
25
|
+
end
|
26
|
+
|
27
|
+
#this must always precede on () calls so they have the instance variables they need
|
28
|
+
options, locs, ridley, logs_bag_hash, pass_bag_hash, bundle_command, cheftacular, passwords = @config['helper'].set_local_instance_vars
|
29
|
+
|
30
|
+
on ( primary_nodes.map { |n| "deploy@" + n.public_ipaddress } ) do |host|
|
31
|
+
n = get_node_from_address(nodes, host.hostname)
|
32
|
+
|
33
|
+
puts "Beginning replication status report run for #{ n.name } (#{ n.public_ipaddress })"
|
34
|
+
|
35
|
+
env = n.name.split('_').first
|
36
|
+
|
37
|
+
rep_status_hash[n.name] = start_replication_report( n.name, n.public_ipaddress, options, locs, passwords)
|
38
|
+
end
|
39
|
+
|
40
|
+
on ( slave_nodes.map { |n| "deploy@" + n.public_ipaddress } ) do |host|
|
41
|
+
n = get_node_from_address(nodes, host.hostname)
|
42
|
+
|
43
|
+
puts "Beginning slave replication status report run for #{ n.name } (#{ n.public_ipaddress })"
|
44
|
+
|
45
|
+
env = n.name.split('_').first
|
46
|
+
|
47
|
+
rep_status_hash[n.name] = start_slave_replication_report( n.name, n.public_ipaddress, options, locs, passwords)
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
rep_status_hash.each_pair do |serv_name, output|
|
52
|
+
out << "#{ serv_name }:"
|
53
|
+
|
54
|
+
output.join("\n").split("\n").each do |line|
|
55
|
+
out << " #{ line }"
|
56
|
+
end
|
57
|
+
|
58
|
+
out << "\n"
|
59
|
+
end
|
60
|
+
|
61
|
+
puts(out)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
module SSHKit
|
67
|
+
module Backend
|
68
|
+
class Netssh
|
69
|
+
|
70
|
+
def start_replication_report name, ip_address, options, locs, passwords, out=[]
|
71
|
+
|
72
|
+
psql_commands = [
|
73
|
+
"select client_addr, state, sent_location, write_location, flush_location, replay_location, sync_priority from pg_stat_replication;",
|
74
|
+
]
|
75
|
+
|
76
|
+
psql_commands.each do |cmnd|
|
77
|
+
out << sudo_capture( passwords[ip_address], :sudo, "su", "-", "postgres", "-c \"psql -c \\\"#{ cmnd }\\\"\"" )
|
78
|
+
end
|
79
|
+
|
80
|
+
out
|
81
|
+
end
|
82
|
+
|
83
|
+
def start_slave_replication_report name, ip_address, options, locs, passwords, out=[]
|
84
|
+
|
85
|
+
psql_commands = [
|
86
|
+
"select now() - pg_last_xact_replay_timestamp() AS replication_delay;",
|
87
|
+
]
|
88
|
+
|
89
|
+
begin
|
90
|
+
psql_commands.each do |cmnd|
|
91
|
+
out << sudo_capture( passwords[ip_address], :sudo, "su", "-", "postgres", "-c \"psql -c \\\"#{ cmnd }\\\"\"" )
|
92
|
+
end
|
93
|
+
rescue StandardError => e
|
94
|
+
out << "This slave database is still setting up its replication! The current status of its process is:"
|
95
|
+
out << capture( :ps, :aux, :|, :grep, :startup, :|, :grep, '-v', :grep)
|
96
|
+
out << "The reason for the failure was: #{ e }"
|
97
|
+
end
|
98
|
+
|
99
|
+
out
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
class Cheftacular
|
2
|
+
class StatelessActionDocumentation
|
3
|
+
def restart_swap
|
4
|
+
@config['documentation']['stateless_action'] << [
|
5
|
+
"`cft restart_swap` will restart the swap on every server that doesn't have swap currently on. " +
|
6
|
+
"Useful if you notice servers with no swap activated from `hip disk_report`",
|
7
|
+
|
8
|
+
[
|
9
|
+
" 1. There is no risk in running this command. Sometimes swap doesnt reactivate if " +
|
10
|
+
"the server was rebooted and this command fixes that."
|
11
|
+
]
|
12
|
+
]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class StatelessAction
|
17
|
+
def restart_swap
|
18
|
+
|
19
|
+
nodes = @config['getter'].get_true_node_objects(true)
|
20
|
+
|
21
|
+
nodes = @config['parser'].exclude_nodes( nodes, [{ if: { env: '_default' }}] )
|
22
|
+
|
23
|
+
#this must always precede on () calls so they have the instance variables they need
|
24
|
+
options, locs, ridley, logs_bag_hash, pass_bag_hash, bundle_command, cheftacular, passwords = @config['helper'].set_local_instance_vars
|
25
|
+
|
26
|
+
on ( nodes.map { |n| "deploy@" + n.public_ipaddress } ), in: :groups, limit: 5, wait: 2 do |host|
|
27
|
+
n = get_node_from_address(nodes, host.hostname)
|
28
|
+
|
29
|
+
puts "Beginning swap restart run for #{ n.name } (#{ n.public_ipaddress })"
|
30
|
+
|
31
|
+
start_swap_restart( n.name, n.public_ipaddress, options, locs, cheftacular, passwords)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
module SSHKit
|
38
|
+
module Backend
|
39
|
+
class Netssh
|
40
|
+
|
41
|
+
def start_swap_restart name, ip_address, options, locs, cheftacular, passwords, out=[]
|
42
|
+
|
43
|
+
if test("[ -e #{ cheftacular['default_swap_location'] } ]") #true if file exists
|
44
|
+
check = capture( :cat, '/proc/swaps' )
|
45
|
+
|
46
|
+
out << sudo_capture( options['pass'][ip_address], :swapon, cheftacular['default_swap_location'] ) unless check.include?(cheftacular['default_swap_location'])
|
47
|
+
|
48
|
+
puts(out.join("\n")) if options['output'] || options['verbose']
|
49
|
+
else
|
50
|
+
puts "Node #{ name } (#{ ip_address }) has not had swap initialized! Skipping..."
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
|
2
|
+
class Cheftacular
|
3
|
+
class StatelessActionDocumentation
|
4
|
+
def server_update
|
5
|
+
@config['documentation']['stateless_action'] << [
|
6
|
+
"`cft server_update [restart]` allows you to force update all nodes' packages for a specific environment. " +
|
7
|
+
"This should be done with caution as this *might* break something.",
|
8
|
+
|
9
|
+
[
|
10
|
+
" 1. `hip apt_update restart` will prompt to ask if you also want to restart all servers in a rolling restart. " +
|
11
|
+
"This should be done with extreme caution and only in a worst-case scenario."
|
12
|
+
]
|
13
|
+
]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class StatelessAction
|
18
|
+
#TODO refactor to handling multiple server types
|
19
|
+
def server_update
|
20
|
+
raise "This action can only be performed if the mode is set to devops" unless @config['helper'].running_in_mode?('devops')
|
21
|
+
|
22
|
+
@options['rolling_restart'] = true if ARGV[1] && ARGV[1] == 'restart'
|
23
|
+
|
24
|
+
if @options['rolling_restart']
|
25
|
+
puts "Preparing to do a rolling restart for all servers in env: #{ @options['env'] } (potential data loss).\nEnter Y/y to confirm, Q/q to exit completely."
|
26
|
+
|
27
|
+
input = STDIN.gets.chomp
|
28
|
+
|
29
|
+
@options['rolling_restart'] = false unless ( input =~ /y|Y|yes|Yes/ ) == 0
|
30
|
+
|
31
|
+
exit if ( input =~ /y|Y|quit|Quit/ ) == 0
|
32
|
+
end
|
33
|
+
|
34
|
+
nodes = @config['getter'].get_true_node_objects true
|
35
|
+
|
36
|
+
nodes = @config['parser'].exclude_nodes( nodes, [{ unless: { env: @options['env'] }}] )
|
37
|
+
|
38
|
+
#this must always precede on () calls so they have the instance variables they need
|
39
|
+
options, locs, ridley, logs_bag_hash, pass_bag_hash, bundle_command, cheftacular, passwords = @config['helper'].set_local_instance_vars
|
40
|
+
|
41
|
+
on ( nodes.map { |n| "deploy@" + n.public_ipaddress } ), in: :groups, limit: 5, wait: 5 do |host|
|
42
|
+
n = get_node_from_address(nodes, host.hostname)
|
43
|
+
|
44
|
+
puts "Beginning apt-get run for #{ n.name } (#{ n.public_ipaddress })"
|
45
|
+
|
46
|
+
log_data, timestamp = start_apt_updater( n.name, n.public_ipaddress, options, locs, passwords)
|
47
|
+
|
48
|
+
logs_bag_hash["#{ n.name }-upgrade"] = { text: log_data.scrub_pretty_text, timestamp: timestamp }
|
49
|
+
end
|
50
|
+
|
51
|
+
on ( nodes.map { |n| "deploy@" + n.public_ipaddress } ), in: :groups, limit: 5, wait: 120 do |host|
|
52
|
+
n = get_node_from_address(nodes, host.hostname)
|
53
|
+
|
54
|
+
puts "Beginning restart run for #{ n.name } (#{ n.public_ipaddress })"
|
55
|
+
|
56
|
+
start_sys_restarter( n.name, n.public_ipaddress, options, locs, passwords)
|
57
|
+
end if @options['rolling_restart']
|
58
|
+
|
59
|
+
@config['ChefDataBag'].save_logs_bag
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
module SSHKit
|
65
|
+
module Backend
|
66
|
+
class Netssh
|
67
|
+
def start_apt_updater name, ip_address, options, locs, passwords, out=""
|
68
|
+
log_loc, timestamp = set_log_loc_and_timestamp(locs)
|
69
|
+
|
70
|
+
puts("Generating apt-get log file for #{ name } (#{ ip_address }) at #{ log_loc }/#{ name }-upgrade-#{ timestamp }.txt") unless options['quiet']
|
71
|
+
|
72
|
+
out << sudo_capture( passwords[ip_address], 'apt-get', 'update' )
|
73
|
+
out << sudo_capture( passwords[ip_address], 'apt-get', 'upgrade', '-y', '-o' 'Dpkg::Options::="--force-confnew"' )
|
74
|
+
|
75
|
+
puts(out) if options['output'] || options['verbose']
|
76
|
+
|
77
|
+
::File.open("#{ log_loc }/#{ name }-apt-update-#{ timestamp }.txt", "w") { |f| f.write(out.scrub_pretty_text) } unless options['no_logs']
|
78
|
+
|
79
|
+
puts "Succeeded upgrade of #{ name } (#{ ip_address })"
|
80
|
+
|
81
|
+
[out, timestamp] #return out to send to logs_bag
|
82
|
+
end
|
83
|
+
|
84
|
+
def start_sys_restarter name, ip_address, options, locs, passwords, out=""
|
85
|
+
log_loc, timestamp = set_log_loc_and_timestamp(locs)
|
86
|
+
|
87
|
+
out << sudo_capture( passwords[ip_address], 'shutdown', '1', '-r' )
|
88
|
+
|
89
|
+
::File.open("#{ log_loc }/#{ name }-upgrade-#{ timestamp }.txt", "w") { |f| f.write(out.scrub_pretty_text) }
|
90
|
+
|
91
|
+
puts(out) if options['output'] || options['verbose']
|
92
|
+
|
93
|
+
puts "Succeeded restart of #{ name } (#{ ip_address })"
|
94
|
+
|
95
|
+
[out, timestamp] #return out to send to logs_bag
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
#boots or destroys the current devstaging environment
|
2
|
+
class Cheftacular
|
3
|
+
class StatelessActionDocumentation
|
4
|
+
def test_env
|
5
|
+
@config['documentation']['stateless_action'] << [
|
6
|
+
"`cft test_env [TARGET_ENV] boot|destroy` will create (or destroy) the test nodes for a particular environment " +
|
7
|
+
"(defaults to staging, prod split-envs can be set with `-p`). Please read below for how TARGET_ENV works",
|
8
|
+
|
9
|
+
[
|
10
|
+
" 1. TARGET_ENV changes functionality depending on the overall (like staging / production) environment",
|
11
|
+
|
12
|
+
" 1. In staging, it cannot be set and defaults to split (splitstaging).",
|
13
|
+
|
14
|
+
" 2. In production, it can be splita, splitb, splitc, or splitd.",
|
15
|
+
|
16
|
+
" 3. The default tld used should change depending on which environment you are booting / destroying. " +
|
17
|
+
"This is set in the environment's config data bag under the tld key"
|
18
|
+
]
|
19
|
+
]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class StatelessAction
|
24
|
+
def test_env split_env="splitstaging", type="boot"
|
25
|
+
raise "This action can only be performed if the mode is set to devops" unless @config['helper'].running_in_mode?('devops')
|
26
|
+
|
27
|
+
env_index = @options['env'] == 'staging' ? 1 : 2
|
28
|
+
|
29
|
+
split_env = ARGV[1] unless @options['env'] == 'staging'
|
30
|
+
|
31
|
+
type = ARGV[env_index] if ARGV[env_index]
|
32
|
+
|
33
|
+
raise "Unknown split_env: #{ split_env }, can only be #{ cheftacular['run_list_environments'].values.join(', ') }" unless (split_env =~ /#{ cheftacular['run_list_environments'].values.join('|') }/) == 0
|
34
|
+
|
35
|
+
raise "Unknown type: #{ type }, can only be 'boot' or 'destroy'" unless (type =~ /boot|destroy/) == 0
|
36
|
+
|
37
|
+
nodes = @config['getter'].get_true_node_objects(true)
|
38
|
+
|
39
|
+
nodes = @config['parser'].exclude_nodes( nodes, [{ unless: "role[#{ split_env.split('split').join('split_') }]" }, { unless: { env: @options['env'] }}])
|
40
|
+
|
41
|
+
@options['force_yes'] = true
|
42
|
+
@options['in_scaling'] = true
|
43
|
+
|
44
|
+
initial_servers = @config['cheftacular']['split_env_nodes']
|
45
|
+
|
46
|
+
case type
|
47
|
+
when 'boot'
|
48
|
+
initial_servers.each_pair do |name, config_hash|
|
49
|
+
true_name = name.gsub('SPLITENV', split_env)
|
50
|
+
@options['node_name'] = "#{ true_name }#{ 'p' if @options['env'] == 'production' }"
|
51
|
+
@options['flavor_name'] = config_hash.has_key?('flavor') ? config_hash['flavor'] : @config['cheftacular']['default_flavor_name']
|
52
|
+
@options['descriptor'] = config_hash.has_key?('descriptor') ? "#{ config_hash['descriptor'] }-#{ split_env }" : name
|
53
|
+
@options['with_dn'] = config_hash.has_key?('dns_config') ? @config['parser'].parse_to_dns(config_hash['dns_config']) : @config['parser'].parse_to_dns('NODE_NAME.ENV_TLD')
|
54
|
+
|
55
|
+
next if nodes.map { |n| n.name }.include?(@options['node_name'])
|
56
|
+
|
57
|
+
puts("Preparing to boot server #{ @options['node_name'] } for #{ @options['env'] }'s #{ split_env } environment!") unless @options['quiet']
|
58
|
+
|
59
|
+
@config['stateless_action'].cloud_bootstrap
|
60
|
+
|
61
|
+
sleep 15
|
62
|
+
end
|
63
|
+
|
64
|
+
@config['ChefDataBag'].save_server_passwords_bag
|
65
|
+
when 'destroy'
|
66
|
+
@options['delete_server_on_remove'] = true
|
67
|
+
|
68
|
+
nodes.each do |node|
|
69
|
+
next if !targets.empty? && !targets.include?(node.name)
|
70
|
+
|
71
|
+
@options['node_name'] = node.name
|
72
|
+
|
73
|
+
puts("Preparing to destroy server #{ @options['node_name'] } for #{ @options['env'] }'s #{ split_env } environment!") unless @options['quiet']
|
74
|
+
|
75
|
+
@config['stateless_action'].remove_client
|
76
|
+
|
77
|
+
sleep 15
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
|
2
|
+
class Cheftacular
|
3
|
+
class StatelessActionDocumentation
|
4
|
+
def update_split_branches
|
5
|
+
@config['documentation']['stateless_action'] << [
|
6
|
+
"`cft update_split_branches` will perform a series of git commands that will merge all the " +
|
7
|
+
"split branches for your split_branch enabled repositories with what is currently on master and push them.",
|
8
|
+
|
9
|
+
[
|
10
|
+
" 1. Repository must be set with `-R REPOSITORY_NAME` for this command to work.",
|
11
|
+
|
12
|
+
" 2. Attempting to run this command in other repositories that do not have the branches listed " +
|
13
|
+
"in run_list_environments OR do not have split_branch set to true will raise an error.",
|
14
|
+
|
15
|
+
" 3. This command will only succeed *IF THERE ARE NO MERGE CONFLICTS*.",
|
16
|
+
|
17
|
+
" 4. This command will return a helpful error statement if you attempt to run the command " +
|
18
|
+
"with changes to your current working directory. You must commit these changes before running this command."
|
19
|
+
]
|
20
|
+
]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class StatelessAction
|
25
|
+
def update_split_branches
|
26
|
+
target_loc = @config['helper'].running_in_mode?('application') ? @config['locs']['app-root'] : "#{ @config['locs']['root'] }/#{ @options['repository'] }"
|
27
|
+
|
28
|
+
current_revision = `cd #{ target_loc } && git rev-parse --abbrev-ref HEAD`
|
29
|
+
|
30
|
+
puts "Preparing to run merges..."
|
31
|
+
|
32
|
+
split_branch_repos = @config['getters'].get_split_branch_hash
|
33
|
+
|
34
|
+
raise "unsupported codebase, please run in #{ split_branch_repos.keys.join(', ') } only!" if ( @options['repository'] =~ /#{ split_branch_repos.keys.join('|') }/ ) == 0
|
35
|
+
|
36
|
+
test_for_changes = `cd #{ target_loc } && git diff --exit-code`
|
37
|
+
|
38
|
+
unless test_for_changes.empty?
|
39
|
+
puts "You have changes in your current working tree for #{ target_loc }. Please commit these changes before running this command."
|
40
|
+
|
41
|
+
exit
|
42
|
+
end
|
43
|
+
|
44
|
+
commands = [
|
45
|
+
"cd #{ target_loc }",
|
46
|
+
"git checkout master",
|
47
|
+
"git pull origin master",
|
48
|
+
"git fetch origin",
|
49
|
+
]
|
50
|
+
|
51
|
+
@config['run_list_environments'].each_key do |branch_name|
|
52
|
+
true_branch_name = branch_name.gsub('_','-')
|
53
|
+
|
54
|
+
commands << ["git checkout #{ true_branch_name }", "git pull origin #{ true_branch_name }", 'git merge master --no-edit', "git push origin #{ true_branch_name }"]
|
55
|
+
end
|
56
|
+
|
57
|
+
commands << "git checkout #{ current_revision }"
|
58
|
+
|
59
|
+
puts `#{ commands.flatten.join(' && ') }` unless @options['quiet']
|
60
|
+
|
61
|
+
puts "Update split branches complete. You have been returned to the branch you were on before which was \"#{ current_revision.chomp }\"."
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
class Cheftacular
|
2
|
+
class StatelessActionDocumentation
|
3
|
+
def update_tld
|
4
|
+
@config['documentation']['stateless_action'] << [
|
5
|
+
"`cft update_tld TLD` command will force a full dns update for a tld in the preferred cloud. " +
|
6
|
+
"It will ensure all the subdomain entries are correct (based on the contents of the addresses data bag) " +
|
7
|
+
"and update them if they are not. It will also create the local subdomain for the entry as well if it " +
|
8
|
+
"does exist and point it to the correct private address."
|
9
|
+
]
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class StatelessAction
|
14
|
+
def update_tld
|
15
|
+
raise "This action can only be performed if the mode is set to devops" unless @config['helper'].running_in_mode?('devops')
|
16
|
+
|
17
|
+
raise "Undefined new tld to migrate to" if ARGV.length <= 1
|
18
|
+
|
19
|
+
nodes = @config['getter'].get_true_node_objects(true)
|
20
|
+
|
21
|
+
#We need to manually update beta nodes as they share the same env space as their non-beta counterparts TODO Refactor?
|
22
|
+
nodes = @config['parser'].exclude_nodes( nodes, [{ unless: { env: @options['env'] }}] )
|
23
|
+
|
24
|
+
addr_data = @config['getter'].get_addresses_hash @options['env']
|
25
|
+
|
26
|
+
tld = ARGV[1]
|
27
|
+
|
28
|
+
nodes.each do |n|
|
29
|
+
|
30
|
+
@options['node_name'] = n.name
|
31
|
+
|
32
|
+
domain_obj = PublicSuffix.parse addr_data[n.public_ipaddress]['dn']
|
33
|
+
|
34
|
+
next unless domain_obj.domain == @config[@options['env']]['config_bag_hash'][@options['sub_env']]['tld'] #we can't create records for domains we dont manage in rax
|
35
|
+
|
36
|
+
@config['stateless_action'].cloud "domain", "create:#{ tld }:#{ domain_obj.trd }:#{ n.public_ipaddress }"
|
37
|
+
|
38
|
+
sleep 5 #don't want to to push updates to rax too fast
|
39
|
+
|
40
|
+
@config['stateless_action'].cloud "domain", "create:#{ tld }:local.#{ domain_obj.trd }:#{ addr_data[n.public_ipaddress]['priv'] }"
|
41
|
+
|
42
|
+
full_domain = "#{ domain_obj.trd }.#{ tld }"
|
43
|
+
|
44
|
+
target_serv_index = @config[@options['env']]['addresses_bag_hash']['addresses'].count
|
45
|
+
|
46
|
+
@config[@options['env']]['addresses_bag_hash']['addresses'].each do |serv_hash|
|
47
|
+
target_serv_index = @config[@options['env']]['addresses_bag_hash']['addresses'].index(serv_hash) if serv_hash['name'] == n.name
|
48
|
+
end
|
49
|
+
|
50
|
+
@config[@options['env']]['addresses_bag_hash']['addresses'][target_serv_hash] ||= {}
|
51
|
+
@config[@options['env']]['addresses_bag_hash']['addresses'][target_serv_hash]['dn'] = full_domain
|
52
|
+
|
53
|
+
sleep 5 #prepare for next domain
|
54
|
+
end
|
55
|
+
|
56
|
+
@config[@options['env']]['config_bag_hash'][@options['sub_env']]['tld'] = tld
|
57
|
+
|
58
|
+
@config['ChefDataBag'].save_config_bag
|
59
|
+
@config['ChefDataBag'].save_addresses_bag
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|