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