cheftacular 2.0.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 +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
|