cheftacular 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (118) hide show
  1. checksums.yaml +7 -0
  2. data/bin/cft +4 -0
  3. data/bin/cftclr +4 -0
  4. data/bin/cheftacular +4 -0
  5. data/bin/client-list +4 -0
  6. data/lib/cheftacular/README.md +416 -0
  7. data/lib/cheftacular/actions/check.rb +32 -0
  8. data/lib/cheftacular/actions/console.rb +62 -0
  9. data/lib/cheftacular/actions/database.rb +13 -0
  10. data/lib/cheftacular/actions/db_console.rb +67 -0
  11. data/lib/cheftacular/actions/deploy.rb +40 -0
  12. data/lib/cheftacular/actions/log.rb +47 -0
  13. data/lib/cheftacular/actions/migrate.rb +57 -0
  14. data/lib/cheftacular/actions/run.rb +64 -0
  15. data/lib/cheftacular/actions/scale.rb +94 -0
  16. data/lib/cheftacular/actions/tail.rb +55 -0
  17. data/lib/cheftacular/actions.rb +14 -0
  18. data/lib/cheftacular/auditor.rb +46 -0
  19. data/lib/cheftacular/chef/data_bag.rb +104 -0
  20. data/lib/cheftacular/cheftacular.rb +55 -0
  21. data/lib/cheftacular/decryptors.rb +45 -0
  22. data/lib/cheftacular/encryptors.rb +48 -0
  23. data/lib/cheftacular/getters.rb +153 -0
  24. data/lib/cheftacular/helpers.rb +296 -0
  25. data/lib/cheftacular/initializers.rb +451 -0
  26. data/lib/cheftacular/parsers.rb +199 -0
  27. data/lib/cheftacular/remote_helpers.rb +30 -0
  28. data/lib/cheftacular/stateless_action.rb +16 -0
  29. data/lib/cheftacular/stateless_actions/add_ssh_key_to_bag.rb +44 -0
  30. data/lib/cheftacular/stateless_actions/arguments.rb +68 -0
  31. data/lib/cheftacular/stateless_actions/backups.rb +116 -0
  32. data/lib/cheftacular/stateless_actions/bootstrappers/centos_bootstrap.rb +7 -0
  33. data/lib/cheftacular/stateless_actions/bootstrappers/coreos_bootstrap.rb +7 -0
  34. data/lib/cheftacular/stateless_actions/bootstrappers/fedora_bootstrap.rb +7 -0
  35. data/lib/cheftacular/stateless_actions/bootstrappers/redhat_bootstrap.rb +7 -0
  36. data/lib/cheftacular/stateless_actions/bootstrappers/ubuntu_bootstrap.rb +102 -0
  37. data/lib/cheftacular/stateless_actions/bootstrappers/vyatta_bootstrap.rb +7 -0
  38. data/lib/cheftacular/stateless_actions/chef_bootstrap.rb +40 -0
  39. data/lib/cheftacular/stateless_actions/chef_environment.rb +21 -0
  40. data/lib/cheftacular/stateless_actions/clean_cookbooks.rb +104 -0
  41. data/lib/cheftacular/stateless_actions/clean_sensu_plugins.rb +19 -0
  42. data/lib/cheftacular/stateless_actions/clean_server_passwords.rb +14 -0
  43. data/lib/cheftacular/stateless_actions/cleanup_log_files.rb +14 -0
  44. data/lib/cheftacular/stateless_actions/client_list.rb +89 -0
  45. data/lib/cheftacular/stateless_actions/cloud.rb +107 -0
  46. data/lib/cheftacular/stateless_actions/cloud_bootstrap.rb +109 -0
  47. data/lib/cheftacular/stateless_actions/compile_audit_log.rb +60 -0
  48. data/lib/cheftacular/stateless_actions/compile_readme.rb +41 -0
  49. data/lib/cheftacular/stateless_actions/create_git_key.rb +67 -0
  50. data/lib/cheftacular/stateless_actions/disk_report.rb +75 -0
  51. data/lib/cheftacular/stateless_actions/environment.rb +100 -0
  52. data/lib/cheftacular/stateless_actions/fetch_file.rb +24 -0
  53. data/lib/cheftacular/stateless_actions/fix_known_hosts.rb +70 -0
  54. data/lib/cheftacular/stateless_actions/full_bootstrap.rb +30 -0
  55. data/lib/cheftacular/stateless_actions/get_active_ssh_connections.rb +18 -0
  56. data/lib/cheftacular/stateless_actions/get_haproxy_log.rb +55 -0
  57. data/lib/cheftacular/stateless_actions/get_log_from_bag.rb +38 -0
  58. data/lib/cheftacular/stateless_actions/get_pg_pass.rb +61 -0
  59. data/lib/cheftacular/stateless_actions/help.rb +71 -0
  60. data/lib/cheftacular/stateless_actions/initialize_data_bag_contents.rb +220 -0
  61. data/lib/cheftacular/stateless_actions/knife_upload.rb +23 -0
  62. data/lib/cheftacular/stateless_actions/pass.rb +49 -0
  63. data/lib/cheftacular/stateless_actions/reinitialize.rb +46 -0
  64. data/lib/cheftacular/stateless_actions/remove_client.rb +81 -0
  65. data/lib/cheftacular/stateless_actions/replication_status.rb +103 -0
  66. data/lib/cheftacular/stateless_actions/restart_swap.rb +55 -0
  67. data/lib/cheftacular/stateless_actions/rvm.rb +14 -0
  68. data/lib/cheftacular/stateless_actions/server_update.rb +99 -0
  69. data/lib/cheftacular/stateless_actions/service.rb +14 -0
  70. data/lib/cheftacular/stateless_actions/test_env.rb +82 -0
  71. data/lib/cheftacular/stateless_actions/update_split_branches.rb +64 -0
  72. data/lib/cheftacular/stateless_actions/update_tld.rb +62 -0
  73. data/lib/cheftacular/stateless_actions/upload_nodes.rb +120 -0
  74. data/lib/cheftacular/stateless_actions/upload_roles.rb +24 -0
  75. data/lib/cheftacular/version.rb +5 -0
  76. data/lib/cheftacular.rb +4 -0
  77. data/lib/cloud_interactor/authentication.rb +56 -0
  78. data/lib/cloud_interactor/cloud_interactor.rb +23 -0
  79. data/lib/cloud_interactor/domain/create.rb +17 -0
  80. data/lib/cloud_interactor/domain/create_record.rb +27 -0
  81. data/lib/cloud_interactor/domain/destroy.rb +17 -0
  82. data/lib/cloud_interactor/domain/destroy_record.rb +23 -0
  83. data/lib/cloud_interactor/domain/list.rb +9 -0
  84. data/lib/cloud_interactor/domain/list_records.rb +22 -0
  85. data/lib/cloud_interactor/domain/read.rb +23 -0
  86. data/lib/cloud_interactor/domain/read_record.rb +27 -0
  87. data/lib/cloud_interactor/domain/update.rb +18 -0
  88. data/lib/cloud_interactor/domain/update_record.rb +42 -0
  89. data/lib/cloud_interactor/domain.rb +18 -0
  90. data/lib/cloud_interactor/flavor.rb +27 -0
  91. data/lib/cloud_interactor/helpers.rb +70 -0
  92. data/lib/cloud_interactor/image.rb +27 -0
  93. data/lib/cloud_interactor/parser.rb +37 -0
  94. data/lib/cloud_interactor/server/attach_volume.rb +33 -0
  95. data/lib/cloud_interactor/server/create.rb +39 -0
  96. data/lib/cloud_interactor/server/destroy.rb +11 -0
  97. data/lib/cloud_interactor/server/detach_volume.rb +21 -0
  98. data/lib/cloud_interactor/server/list.rb +7 -0
  99. data/lib/cloud_interactor/server/list_volumes.rb +25 -0
  100. data/lib/cloud_interactor/server/poll.rb +22 -0
  101. data/lib/cloud_interactor/server/read.rb +9 -0
  102. data/lib/cloud_interactor/server/read_volume.rb +24 -0
  103. data/lib/cloud_interactor/server.rb +17 -0
  104. data/lib/cloud_interactor/version.rb +4 -0
  105. data/lib/cloud_interactor/volume/create.rb +13 -0
  106. data/lib/cloud_interactor/volume/destroy.rb +11 -0
  107. data/lib/cloud_interactor/volume/list.rb +7 -0
  108. data/lib/cloud_interactor/volume/read.rb +9 -0
  109. data/lib/cloud_interactor/volume.rb +20 -0
  110. data/lib/ridley/monkeypatches.rb +11 -0
  111. data/lib/sshkit/actions/start_commit_check.rb +19 -0
  112. data/lib/sshkit/actions/start_deploy.rb +25 -0
  113. data/lib/sshkit/actions/start_log_fetch.rb +91 -0
  114. data/lib/sshkit/actions/start_task.rb +29 -0
  115. data/lib/sshkit/getters.rb +67 -0
  116. data/lib/sshkit/helpers.rb +13 -0
  117. data/lib/sshkit/monkeypatches.rb +19 -0
  118. 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,14 @@
1
+ #interact with an rvm installation on servers
2
+ class Cheftacular
3
+ class StatelessActionDocumentation
4
+ def rvm
5
+ #TODO
6
+ end
7
+ end
8
+
9
+ class StatelessAction
10
+ def rvm action="list"
11
+
12
+ end
13
+ end
14
+ 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,14 @@
1
+
2
+ class Cheftacular
3
+ class StatelessActionDocumentation
4
+ def service
5
+
6
+ end
7
+ end
8
+
9
+ class StatelessAction
10
+ def service
11
+ #TODO
12
+ end
13
+ end
14
+ 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