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.
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