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,89 @@
1
+
2
+ class Cheftacular
3
+ class StatelessActionDocumentation
4
+ def client_list
5
+ @config['documentation']['stateless_action'] << [
6
+ "`cft client_list` Allows you check the basic information for all the servers setup via chef. " +
7
+ "Shows the server's short name, its public ip address and roles (run_list) by default.",
8
+
9
+ [
10
+ " 1. `-v` option will make this command display the server's domain name, " +
11
+ "whether its password is stored on the chef server and what that password is.",
12
+
13
+ " 2. `-W|--with-priv` option will make this command display the server's local (private) ip address. " +
14
+ "This address is also the server's `local.<SERVER_DNS_NAME>`.",
15
+
16
+ " 3. This command is aliased to `client-list` with no arguments or cft prefix."
17
+ ]
18
+ ]
19
+
20
+ @config['documentation']['application'] << @config['documentation']['stateless_action'].last
21
+ end
22
+ end
23
+
24
+ class StatelessAction
25
+ def client_list
26
+ @config['helper'].cleanup_file_caches('current')
27
+
28
+ nodes = @config['getter'].get_true_node_objects(true)
29
+
30
+ @config['chef_environments'].each do |env|
31
+ @config['initializer'].initialize_data_bags_for_environment env, false, ['addresses', 'server_passwords']
32
+ end
33
+
34
+ environments = nodes.map { |n| n.chef_environment }
35
+
36
+ environments.uniq.each do |env|
37
+ next if env == '_default'
38
+
39
+ env_nodes = @config['parser'].exclude_nodes(nodes, [{ if: { not_env: env } }])
40
+ puts "\nFound #{ env_nodes.count } #{ env } nodes:"
41
+ out = " #{ 'name'.ljust(22) } #{ 'ip_address'.ljust(21) }"
42
+ out << "#{ 'private_address'.ljust(21) }" if @options['with_private']
43
+ out << "#{ 'pass?'.ljust(5) } #{ 'domain'.ljust(41) }" if @options['verbose']
44
+ out << "#{ 'deploy_password'.ljust(21) }" if @options['verbose']
45
+ out << "run_list"
46
+
47
+ puts out
48
+
49
+ auth_hash = @config[env]['server_passwords_bag_hash']
50
+
51
+ addr_data = @config['getter'].get_addresses_hash env
52
+
53
+ env_nodes.each do |node|
54
+ #client = @ridley.client.find(options['node_name'])
55
+ out = " #{ node.chef_id.ljust(22,'_') }_#{ node.public_ipaddress.ljust(20,'_') }"
56
+
57
+ if @options['with_private']
58
+ if addr_data.has_key?(node.public_ipaddress)
59
+ out << addr_data[node.public_ipaddress]['priv'].ljust(20,'_')
60
+ else
61
+ out << ''.ljust(20,'_')
62
+ end
63
+ end
64
+
65
+ if @options['verbose']
66
+
67
+ out << "_" + auth_hash.has_key?("#{ node.public_ipaddress }-deploy-pass").to_s.ljust(5,'_') + "_"
68
+
69
+ if addr_data.has_key?(node.public_ipaddress)
70
+ out << addr_data[node.public_ipaddress]['dn'].ljust(40,'_')
71
+ else
72
+ out << ''.ljust(40,'_')
73
+ end
74
+
75
+ if auth_hash.has_key?("#{ node.public_ipaddress }-deploy-pass")
76
+ out << "_" + auth_hash["#{ node.public_ipaddress }-deploy-pass"]
77
+ else
78
+ out << "_" + ''.ljust(@config['cheftacular']['server_password_length'],'_')
79
+ end
80
+ end
81
+
82
+ out << "_#{ node.run_list.join(', ') }"
83
+
84
+ puts out
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,107 @@
1
+
2
+ class Cheftacular
3
+ class StatelessActionDocumentation
4
+ def cloud
5
+ @config['documentation']['stateless_action'] << [
6
+ "`cft cloud <FIRST_LEVEL_ARG> [<SECOND_LEVEL_ARG>[:<SECOND_LEVEL_ARG_QUERY>]*] ` this command handles talking to various cloud apis. " +
7
+ "If no args are passed nothing will happen.",
8
+
9
+ [
10
+ " 1. `domain` 1st level argument for interacting with cloud domains",
11
+
12
+ " 1. `list` default behavior",
13
+
14
+ " 2. `read:TOP_LEVEL_DOMAIN` returns detailed information about all subdomains attached to the TOP_LEVEL_DOMAIN",
15
+
16
+ " 3. `read_record:TOP_LEVEL_DOMAIN:QUERY_STRING` queries the top level domain for all subdomains that have the QUERY_STRING in them.",
17
+
18
+ " 4. `create:TOP_LEVEL_DOMAIN` creates the top level domain on rackspace",
19
+
20
+ " 5. `create_record:TOP_LEVEL_DOMAIN:SUBDOMAIN_NAME:IP_ADDRESS[:RECORD_TYPE[:TTL]]` " +
21
+ "IE: `cft cloud domain create:mydomain.com:myfirstserver:1.2.3.4` will create the subdomain 'myfirstserver' on the mydomain.com domain.",
22
+
23
+ " 6. `destroy:TOP_LEVEL_DOMAIN` destroys the top level domain and all of its subdomains",
24
+
25
+ " 7. `destroy_record:TOP_LEVEL_DOMAIN:SUBDOMAIN_NAME` deletes the subdomain record for TOP_LEVEL_DOMAIN if it exists.",
26
+
27
+ " 8. `update:TOP_LEVEL_DOMAIN` takes the value of the email in the authentication data bag for your specified cloud and updates the TLD.",
28
+
29
+ " 9. `update_record:TOP_LEVEL_DOMAIN:SUBDOMAIN_NAME:IP_ADDRESS[:RECORD_TYPE[:TTL]]` similar to `create_record`.",
30
+
31
+ " 2. `server` 1st level argument for interacting with cloud servers, " +
32
+ "if no additional args are passed the command will return a list of all servers on the preferred cloud.",
33
+
34
+ " 1. `list` default behavior",
35
+
36
+ " 2. `read:SERVER_NAME` returns all servers that have SERVER_NAME in them (you want to be as specific as possible for single matches)",
37
+
38
+ " 3. `create:SERVER_NAME:FLAVOR_ALIAS` IE: `cft cloud server \"create:myserver:1 GB Performance\"` " +
39
+ "will create a server with the name myserver and the flavor \"1 GB Performance\". Please see flavors section.",
40
+
41
+ " 1. NOTE! If you forget to pass in a flavor alias the script will not error! It will attempt to create a 512MB Standard Instance!",
42
+
43
+ " 2. NOTE! Most flavors have spaces in them, you must use quotes at the command line to utilize them!",
44
+
45
+ " 4. `destroy:SERVER_NAME` destroys the server on the cloud. This must be an exact match of the server's actual name or the script will error.",
46
+
47
+ "  5. `poll:SERVER_NAME` polls the cloud's server for the status of the SERVER_NAME. " +
48
+ "This command will stop polling if / when the status of the server is ACTIVE and its build progress is 100%.",
49
+
50
+ " 6. `attach_volume:SERVER_NAME:VOLUME_NAME[:VOLUME_SIZE[:DEVICE_LOCATION]]` " +
51
+ "If VOLUME_NAME exists it will attach it if it is unattached otherwise it will create it",
52
+
53
+ " 1. NOTE! If the system creates a volume the default size is 100 GB!",
54
+
55
+ " 2. DEVICE_LOCATION refers to the place the volume will be mounted on, a place like `/dev/xvdb`, " +
56
+ "from here it must be added to the filesystem to be used.",
57
+
58
+ " 3. If you want to specify a location, you must specify a size, if the volume already exists it wont be resized but will be attached at that location!",
59
+
60
+ " 4. If DEVICE_LOCATION is blank the volume will be attached to the first available slot.",
61
+
62
+ " 7. `detach_volume:SERVER_NAME:VOLUME_NAME` Removes the volume from the server if it is attached. " +
63
+ "If this operation is performed while the volume is mounted it could corrupt the volume! Do not do this unless you know exactly what you're doing!",
64
+
65
+ " 8. `list_volumes:SERVER_NAME` lists all volumes attached to a server",
66
+
67
+ " 9. `read_volume:SERVER_NAME:VOLUME_NAME` returns the data of VOLUME_NAME if it is attached to the server.",
68
+
69
+ " 3. `volume` 1st level argument for interacting with cloud storage volumes, if no additional args are passed the command will return a list of all cloud storage containers.",
70
+
71
+ " 1. `list` default behavior",
72
+
73
+ " 2. `read:VOLUME_NAME` returns the details for a specific volume.",
74
+
75
+ " 3. `create:VOLUME_NAME:VOLUME_SIZE` IE `cft rax volume create:staging_db:256`",
76
+
77
+ " 4. `destroy:VOLUME_NAME` destroys the volume. This operation will not work if the volume is attached to a server.",
78
+
79
+ " 4. `flavor` 1st level argument for listing the flavors available on the cloud service",
80
+
81
+ " 1. `list` default behavior",
82
+
83
+ " 2. `read:FLAVOR SIZE` behaves the same as list unless a flavor size is supplied.",
84
+
85
+ " 1. Standard servers are listed as XGB with no spaces in their size, performance servers are listed as X GB with " +
86
+ "a space in their size. If you are about to create a server and are unsure, query flavors first."
87
+ ]
88
+ ]
89
+ end
90
+ end
91
+
92
+ class StatelessAction
93
+ def cloud *args
94
+ raise "This action can only be performed if the mode is set to devops" if !@config['helper'].running_in_mode?('devops') && !@options['in_scaling']
95
+
96
+ args = ARGV[1..ARGV.length] if args.empty?
97
+
98
+ @config['cloud_interactor'] ||= CloudInteractor.new(@config['default']['authentication_bag_hash'], @options)
99
+
100
+ @config['cloud_interactor'].run args
101
+ end
102
+
103
+ alias_method :aws, :cloud
104
+
105
+ alias_method :rax, :cloud
106
+ end
107
+ end
@@ -0,0 +1,109 @@
1
+
2
+ class Cheftacular
3
+ class StatelessActionDocumentation
4
+ def cloud_bootstrap
5
+ @config['documentation']['stateless_action'] << [
6
+ "`cft cloud_bootstrap NODE_NAME FLAVOR_NAME [DESCRIPTOR] [--with-dn DOMAIN]` uses a cloud api to " +
7
+ "create a server and attaches its DOMAIN_NAME to the TLD specified for that environment (IE: example-staging.com for staging)",
8
+
9
+ [
10
+ " 1. If no DOMAIN_NAME is supplied it will use the node's NODE_NAME (IE: api01.example-staging.com)",
11
+
12
+ " 2. If the `--with-dn DOMAIN` argument is supplied the rax api will attempt to attach the node to the " +
13
+ "top level domain instead of the default environment one. This tld must be attached to the cloud service. "+
14
+ "This also allows you to attach to custom subdomains instead of NODE_NAME.ENV_TLD",
15
+
16
+ " 3. `cft cloud_bootstrap myserver \"1 GB Performance\" --with-dn myserver.example-staging.com` " +
17
+ 'The "1 GB Perfomance" does not have to be exact, "1 GB" will match "1 GB Performance" and "1GB" ' +
18
+ "will match \"1GB Standard\" (for rackspace flavors)",
19
+
20
+ " 4. DESCRIPTOR is used as an internal tag for the node, if left blank it will become the name of the node. " +
21
+ "It is recommended to enter a custom repository-dependent tag here to make nodes easier to load-balance like \"lb:[CODEBASE_NAME]\""
22
+ ]
23
+ ]
24
+ end
25
+ end
26
+
27
+ class StatelessAction
28
+ def cloud_bootstrap
29
+ raise "This action can only be performed if the mode is set to devops" if !@config['helper'].running_in_mode?('devops') && !@options['in_scaling']
30
+
31
+ @options['node_name'] = ARGV[1] unless @options['node_name']
32
+ @options['flavor_name'] = ARGV[2] unless @options['flavor_name']
33
+ @options['descriptor'] = ARGV[3] if ARGV[3] && !@options['descriptor']
34
+ full_domain = ""
35
+
36
+ if `which sshpass`.empty?
37
+ raise "sshpass not installed! Please run brew install https://raw.github.com/eugeneoden/homebrew/eca9de1/Library/Formula/sshpass.rb (or get it from your repo for linux)"
38
+ end
39
+
40
+ #the output of the cloud command is a hash, this hash is UPDATED every time a rax command is run so you only need to grab it when you need it
41
+ @config['stateless_action'].cloud "server", "create:#{ @options['env'] }_#{ @options['node_name'] }:#{ @options['flavor_name'] }"
42
+
43
+ status_hash = @config['stateless_action'].cloud "server", "poll:#{ @options['env'] }_#{ @options['node_name'] }"
44
+
45
+ status_hash['created_servers'].each do |server_hash|
46
+ next unless server_hash['name'] == "#{ @options['env'] }_#{ @options['node_name'] }"
47
+
48
+ @options['address'] = server_hash['ipv4_address']
49
+
50
+ @options['private_address'] = server_hash['addresses']['private'][0]['addr']
51
+ end
52
+
53
+ begin
54
+ @options['client_pass'] = status_hash['admin_passwords']["#{ @options['env'] }_#{ @options['node_name'] }"]
55
+ rescue NoMethodError => e
56
+ puts "Unable to locate an admin pass for server #{ @options['node_name'] }, does the server already exist? Exiting #{ __method__ }..."
57
+
58
+ return false
59
+ end
60
+
61
+ if @options['with_dn']
62
+ domain_obj = PublicSuffix.parse @options['with_dn']
63
+
64
+ @config['stateless_action'].cloud "domain", "create_record:#{ domain_obj.domain }:#{ domain_obj.trd }:#{ @options['address'] }"
65
+
66
+ sleep 5
67
+
68
+ @config['stateless_action'].cloud "domain", "create_record:#{ domain_obj.domain }:local.#{ domain_obj.trd }:#{ @options['private_address'] }"
69
+
70
+ full_domain = @options['with_dn']
71
+
72
+ #set the wildcard domain for frontend load balancers
73
+ @config['stateless_action'].cloud "domain", "create_record:#{ domain_obj.domain }:*:#{ @options['address'] }" if ( @options['node_name'] =~ /mysc.*lb/ ) == 0
74
+ end
75
+
76
+ tld = @config[@options['env']]['config_bag_hash'][@options['sub_env']]['tld']
77
+
78
+ @config['stateless_action'].cloud "domain", "create_record:#{ tld }:#{ @options['node_name'] }:#{ @options['address'] }"
79
+
80
+ sleep 5
81
+
82
+ @config['stateless_action'].cloud "domain", "create_record:#{ tld }:local.#{ @options['node_name'] }:#{ @options['private_address'] }"
83
+
84
+ full_domain = "#{ @options['node_name'] }.#{ tld }" if full_domain.blank?
85
+
86
+ @config[@options['env']]['addresses_bag_hash'] = @config[@options['env']]['addresses_bag'].reload.to_hash
87
+
88
+ target_serv_index = @config[@options['env']]['addresses_bag_hash']['addresses'].count
89
+
90
+ @config[@options['env']]['addresses_bag_hash']['addresses'].each do |serv_hash|
91
+ target_serv_index = @config[@options['env']]['addresses_bag_hash']['addresses'].index(serv_hash) if serv_hash['name'] == @options['node_name']
92
+ end
93
+
94
+ #EX: "name": "api1", "public": "1.2.3.4", "address": "10.208.1.2", "dn":"api1.example.com", "descriptor": "lb:my-backend-codebase"
95
+ @config[@options['env']]['addresses_bag_hash']['addresses'][target_serv_index] ||= {}
96
+ @config[@options['env']]['addresses_bag_hash']['addresses'][target_serv_index]['name'] = @options['node_name']
97
+ @config[@options['env']]['addresses_bag_hash']['addresses'][target_serv_index]['public'] = @options['address']
98
+ @config[@options['env']]['addresses_bag_hash']['addresses'][target_serv_index]['address'] = @options['private_address']
99
+ @config[@options['env']]['addresses_bag_hash']['addresses'][target_serv_index]['dn'] = full_domain
100
+ @config[@options['env']]['addresses_bag_hash']['addresses'][target_serv_index]['descriptor'] = @options['descriptor'].nil? ? @options['node_name'] : @options['descriptor']
101
+
102
+ @config['ChefDataBag'].save_addresses_bag
103
+
104
+ @options['dont_remove_address_or_server'] = true #flag to make sure our entry isnt removed in addresses bag
105
+
106
+ full_bootstrap #bootstrap server with ruby and attach it to the chef server
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,60 @@
1
+ class Cheftacular
2
+ class StatelessActionDocumentation
3
+ def compile_audit_log
4
+ @config['documentation']['stateless_action'] << [
5
+ "`cft compile_audit_log [clean]` compiles the audit logs in each environment's " +
6
+ "audit data bag a audit-log-CURRENTDAY.md file in the log folder of the application. Bear in mind that the bag " +
7
+ "can only hold 100K bytes and will need to have that data removed to store more than that."
8
+ ]
9
+ end
10
+ end
11
+
12
+ class StatelessAction
13
+ def compile_audit_log out=[]
14
+ compiled_audit_hash = {}
15
+
16
+ @config['chef_environments'].each do |env|
17
+ @config['initializer'].initialize_data_bags_for_environment env, false, ['audit']
18
+
19
+ @config['initializer'].initialize_audit_bag_contents env
20
+
21
+ @config[env]['audit_bag_hash']['audit_log'].each_pair do |day, time_log_hash|
22
+ compiled_audit_hash[day] ||= {}
23
+ time_log_hash.each_pair do |time, log_array|
24
+ compiled_audit_hash[day][time] ||= []
25
+ compiled_audit_hash[day][time] << log_array
26
+ end
27
+ end
28
+ end
29
+
30
+ compiled_audit_hash.each_pair do |day, time_log_hash|
31
+ out << "# Audit Log Entries for #{ Date.parse(day) }"
32
+
33
+ entry_count, int_times = 1, []
34
+
35
+ time_log_hash.keys.each do |time|
36
+ out << "#{ entry_count }. #{ time }"
37
+
38
+ log_array_entry_count = 1
39
+
40
+ time_log_hash[time].each do |log_arr|
41
+ log_arr.each do |log_hash|
42
+ out << " #{ log_array_entry_count }. #{ log_hash['command'] }"
43
+ out << " 1. Hostname: #{ log_hash['hostname'] }"
44
+ out << " 2. IPAddress: #{ log_hash['true_ip'] }"
45
+ out << " 3. Arguments: #{ log_hash['arguments'] }"
46
+ out << " 4. Options: #{ log_hash['options'].to_hash }"
47
+ out << ""
48
+
49
+ log_array_entry_count += 1
50
+ end
51
+ end if time_log_hash.has_key?(time)
52
+
53
+ entry_count += 1
54
+ end
55
+ end
56
+
57
+ File.open("#{ @config['locs']['chef-log'] }/audit-log-#{ Time.now.strftime("%Y%m%d%H%M%S") }.md", "w") { |f| f.write(out.join("\n")) }
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,41 @@
1
+ class Cheftacular
2
+ class StatelessActionDocumentation
3
+ def compile_readme
4
+ @config['documentation']['stateless_action'] << [
5
+ "`cft compile_readme` compiles all documentation methods and creates a README.md file in the log folder of the application."
6
+ ]
7
+ end
8
+ end
9
+
10
+ class StatelessAction
11
+ def compile_readme out=[]
12
+ @config['action_documentation'].public_methods(false).each do |method|
13
+ @config['action_documentation'].send(method)
14
+ end
15
+
16
+ @config['stateless_action_documentation'].public_methods(false).each do |method|
17
+ @config['stateless_action_documentation'].send(method)
18
+ end
19
+
20
+ out << '# Table of Contents for Cheftacular Commands'
21
+
22
+ out << '1. [Cheftacular Arguments and Flags](https://github.com/SocialCentivPublic/cheftacular/blob/master/lib/cheftacular/README.md#arguments-and-flags-for-cheftacular))'
23
+
24
+ out << '2. [Application Commands](https://github.com/SocialCentivPublic/cheftacular/blob/master/lib/cheftacular/README.md#commands-that-can-be-run-in-the-application-context)'
25
+
26
+ out << '3. [DevOps Commands](https://github.com/SocialCentivPublic/cheftacular/blob/master/lib/cheftacular/README.md#commands-that-can-only-be-run-in-the-devops-context))' + "\n"
27
+
28
+ out << @config['documentation']['arguments']
29
+
30
+ out << "\n## Commands that can be run in the application context"
31
+
32
+ out << @config['helper'].compile_documentation_lines('application')
33
+
34
+ out << "\n## Commands that can ONLY be run in the devops context"
35
+
36
+ out << @config['helper'].compile_documentation_lines('stateless_action')
37
+
38
+ File.open("#{ @config['locs']['chef-log'] }/README.md", "w") { |f| f.write(out.flatten.join("\n\n")) }
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,67 @@
1
+
2
+ #Uploads a private key found in ~/.ssh/id_rsa to an encrypted bag to let the chef-server access private repos.
3
+ class Cheftacular
4
+ class StatelessActionDocumentation
5
+ def create_git_key
6
+ @config['documentation']['stateless_action'] << [
7
+ "`cft create_git_key ID_RSA_FILE [OAUTH_TOKEN]` This command will update the default/authentication data bag with new credentials. " +
8
+ "The [ID_RSA_FILE](https://help.github.com/articles/generating-ssh-keys) needs to exist beforehand.",
9
+
10
+ [
11
+ " 1. This command will upload both the private and public key to the data bag. " +
12
+ "The public key should be the one that matches the github user for your deployment github user.",
13
+
14
+ " 2. `OAUTH_TOKEN` *must* be generated by logging into github and generating an access token in the account settings -> applications -> personal access tokens"
15
+ ]
16
+ ]
17
+ end
18
+ end
19
+
20
+ class StatelessAction
21
+ def create_git_key oauth_key=""
22
+
23
+ case ARGV[1]
24
+ when nil then raise "Too few arguments, please enter the filename of the id_rsa file you want to use"
25
+ when 'id_rsa' then raise "Sorry, you can't use your default id_rsa"
26
+ else key_file = ARGV[1]
27
+ end
28
+
29
+ case ARGV[2]
30
+ when nil then display_oauth_notice = true
31
+ else oauth_key = ARGV[2]
32
+ end
33
+
34
+ data = File.read("#{ @config['locs']['chef'] }/#{ key_file }")
35
+ data_pub = File.read("#{ @config['locs']['chef'] }/#{ key_file }.pub")
36
+
37
+ hash = @config['default']['authentication_bag_hash']
38
+
39
+ if h.has_key?('private_key')
40
+ puts "Overwrite current git key in default data bag? (Y/y/N/n)"
41
+ input = STDIN.gets.chomp
42
+
43
+ overwrite = (input =~ /Y|y|yes/) == 0
44
+ else overwrite = true
45
+ end
46
+
47
+ if overwrite
48
+
49
+ hash['private_key'] = data
50
+
51
+ hash['public_key'] = data_pub
52
+
53
+ hash['OAuth'] = oauth_key
54
+
55
+ @config['ChefDataBag'].save_authentication_bag
56
+
57
+ if oauth_key.blank?
58
+ puts "REMEMBER! You need to put a OAuth token into this data bag item!"
59
+ puts "You need to go to github and get the auth_token for the hiplogiq deploy user!"
60
+ puts "Copy the key and paste it inbetween the quotes.\n"
61
+ puts "\"Oauth\": \"<PASTE YOUR OAUTH KEY HERE>\"\n\n"
62
+ puts "Please run \nknife data bag edit default authentication --secret-file #{ @config['locs']['chef'] }/#{ @config['cheftacular']['data_bag_key_file'] }\n"
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,75 @@
1
+ class Cheftacular
2
+ class StatelessActionDocumentation
3
+ def disk_report
4
+ @config['documentation']['stateless_action'] << [
5
+ "`cft disk_report` will fetch useful statistics from every server for every environment and output it into your log directory."
6
+ ]
7
+
8
+ @config['documentation']['application'] << @config['documentation']['stateless_action'].last
9
+ end
10
+ end
11
+
12
+ class StatelessAction
13
+ def disk_report disk_hash={}, out=[]
14
+
15
+ nodes = @config['getter'].get_true_node_objects(true)
16
+
17
+ nodes = @config['parser'].exclude_nodes( nodes, [{ if: { env: '_default' }}] )
18
+
19
+ @config['chef_environments'].each do |env|
20
+ @config['initializer'].initialize_data_bags_for_environment env, false, ['addresses', 'server_passwords']
21
+
22
+ @config['initializer'].initialize_passwords env
23
+ end
24
+
25
+ options, locs, ridley, logs_bag_hash, pass_bag_hash, bundle_command, cheftacular, passwords = @config['helper'].set_local_instance_vars
26
+
27
+ on ( nodes.map { |n| "deploy@" + n.public_ipaddress } ), in: :groups, limit: 5, wait: 2 do |host|
28
+ n = get_node_from_address(nodes, host.hostname)
29
+
30
+ puts "Beginning disk report run for #{ n.name } (#{ n.public_ipaddress })"
31
+
32
+ disk_hash[n.name] = start_disk_report( n.name, n.public_ipaddress, options, locs, passwords)
33
+ end
34
+
35
+ disk_hash.each_pair do |serv_name, output|
36
+ out << "#{ serv_name }:"
37
+
38
+ line_count = 1
39
+
40
+ output.join("\n").split("\n").each do |line|
41
+ out << line_count == 1 ? " #{ line }" : " #{ line }"
42
+
43
+ line_count += 1
44
+ end
45
+
46
+ out << "\n"
47
+ end
48
+
49
+ puts(out) if @options['no_logs'] || @options['verbose']
50
+
51
+ log_loc, timestamp = @config['helper'].set_log_loc_and_timestamp
52
+
53
+ puts("Generating log file for disk report at #{ log_loc }/disk-report-#{ timestamp }.txt") unless @options['quiet']
54
+
55
+ File.open("#{ log_loc }/disk-report-#{ timestamp }.txt", "w") { |f| f.write(out.join("\n").scrub_pretty_text) } unless @options['no_logs']
56
+ end
57
+ end
58
+ end
59
+
60
+ module SSHKit
61
+ module Backend
62
+ class Netssh
63
+ def start_disk_report name, ip_address, options, locs, passwords, out=[]
64
+
65
+ out << sudo_capture( passwords[ip_address], :free, '-m' )
66
+ out << "\n"
67
+ out << sudo_capture( passwords[ip_address], :df, '-aTh' )
68
+
69
+ puts(out.join("\n")) if options['output'] || options['verbose']
70
+
71
+ out
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,100 @@
1
+
2
+ class Cheftacular
3
+ class StatelessActionDocumentation
4
+ def environment
5
+ @config['documentation']['stateless_action'] << [
6
+ "`cft environment boot|destroy` will boot / destroy the current environment",
7
+
8
+ [
9
+ " 1. `boot` will spin up servers and bring them to a stable state. " +
10
+ "This includes setting up their subdomains for the target environment.",
11
+
12
+ " 2. `destroy` will destroy all servers needed for the target environment",
13
+
14
+ " 3. This command will prompt when attempting to destroy servers in staging or production"
15
+ ]
16
+ ]
17
+
18
+ @config['documentation']['application'] << @config['documentation']['stateless_action'].last
19
+ end
20
+ end
21
+
22
+ class StatelessAction
23
+ def environment type="boot", ask_on_destroy=false, remove=true
24
+ ask_on_destroy = case @options['env']
25
+ when 'staging' then true
26
+ when 'production' then true
27
+ else false
28
+ end
29
+
30
+ type = ARGV[1] if ARGV[1]
31
+
32
+ raise "Unknown type: #{ type }, can only be 'boot' or 'destroy'" unless (type =~ /boot|destroy/) == 0
33
+
34
+ nodes = @config['getter'].get_true_node_objects(true)
35
+
36
+ nodes = @config['parser'].exclude_nodes( nodes, [{ unless: { env: @options['env'] }}])
37
+
38
+ @options['force_yes'] = true
39
+ @options['in_scaling'] = true
40
+
41
+ initial_servers = @config['cheftacular']['env_boot_nodes']["#{ @options['env'] }_node_names"]
42
+
43
+ if initial_servers.empty?
44
+ puts "There are no servers defined for #{ @options['env'] } in the env_boot_nodes hash in your cheftacular.yml... Exiting"
45
+
46
+ exit
47
+ end
48
+
49
+ case type
50
+ when 'boot'
51
+ initial_servers.each_pair do |name, config_hash|
52
+ @options['node_name'] = name
53
+ @options['flavor_name'] = config_hash.has_key?('flavor') ? config_hash['flavor'] : @config['cheftacular']['default_flavor_name']
54
+ @options['descriptor'] = config_hash.has_key?('descriptor') ? config_hash['descriptor'] : name
55
+ @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')
56
+
57
+ next if nodes.map { |n| n.name }.include?(@options['node_name'])
58
+
59
+ puts("Preparing to boot server #{ @options['node_name'] } for #{ @options['env'] }!") unless @options['quiet']
60
+
61
+ @config['stateless_action'].cloud_bootstrap
62
+
63
+ sleep 15
64
+ end
65
+
66
+ @config['ChefDataBag'].save_server_passwords_bag
67
+
68
+ @options['node_name'] = nil
69
+
70
+ @options['role'] = 'all'
71
+
72
+ @config['action'].deploy
73
+
74
+ #TODO INTEGRATE backups TO LOAD DATA INTO THE NEWLY BOOTED ENV
75
+ when 'destroy'
76
+ if ask_on_destroy
77
+ puts "Preparing to delete nodes in #{ @options['env'] }.\nEnter Y/y to confirm."
78
+
79
+ input = STDIN.gets.chomp
80
+
81
+ remove = false unless ( input =~ /y|Y|yes|Yes/ ) == 0
82
+ end
83
+
84
+ return false unless remove
85
+
86
+ @options['delete_server_on_remove'] = true
87
+
88
+ nodes.each do |node|
89
+ @options['node_name'] = node.name
90
+
91
+ puts("Preparing to destroy server #{ @options['node_name'] } for #{ @options['env'] }!") unless @options['quiet']
92
+
93
+ @config['stateless_action'].remove_client
94
+
95
+ sleep 15
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end