cheftacular 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/bin/cft +4 -0
- data/bin/cftclr +4 -0
- data/bin/cheftacular +4 -0
- data/bin/client-list +4 -0
- data/lib/cheftacular/README.md +416 -0
- data/lib/cheftacular/actions/check.rb +32 -0
- data/lib/cheftacular/actions/console.rb +62 -0
- data/lib/cheftacular/actions/database.rb +13 -0
- data/lib/cheftacular/actions/db_console.rb +67 -0
- data/lib/cheftacular/actions/deploy.rb +40 -0
- data/lib/cheftacular/actions/log.rb +47 -0
- data/lib/cheftacular/actions/migrate.rb +57 -0
- data/lib/cheftacular/actions/run.rb +64 -0
- data/lib/cheftacular/actions/scale.rb +94 -0
- data/lib/cheftacular/actions/tail.rb +55 -0
- data/lib/cheftacular/actions.rb +14 -0
- data/lib/cheftacular/auditor.rb +46 -0
- data/lib/cheftacular/chef/data_bag.rb +104 -0
- data/lib/cheftacular/cheftacular.rb +55 -0
- data/lib/cheftacular/decryptors.rb +45 -0
- data/lib/cheftacular/encryptors.rb +48 -0
- data/lib/cheftacular/getters.rb +153 -0
- data/lib/cheftacular/helpers.rb +296 -0
- data/lib/cheftacular/initializers.rb +451 -0
- data/lib/cheftacular/parsers.rb +199 -0
- data/lib/cheftacular/remote_helpers.rb +30 -0
- data/lib/cheftacular/stateless_action.rb +16 -0
- data/lib/cheftacular/stateless_actions/add_ssh_key_to_bag.rb +44 -0
- data/lib/cheftacular/stateless_actions/arguments.rb +68 -0
- data/lib/cheftacular/stateless_actions/backups.rb +116 -0
- data/lib/cheftacular/stateless_actions/bootstrappers/centos_bootstrap.rb +7 -0
- data/lib/cheftacular/stateless_actions/bootstrappers/coreos_bootstrap.rb +7 -0
- data/lib/cheftacular/stateless_actions/bootstrappers/fedora_bootstrap.rb +7 -0
- data/lib/cheftacular/stateless_actions/bootstrappers/redhat_bootstrap.rb +7 -0
- data/lib/cheftacular/stateless_actions/bootstrappers/ubuntu_bootstrap.rb +102 -0
- data/lib/cheftacular/stateless_actions/bootstrappers/vyatta_bootstrap.rb +7 -0
- data/lib/cheftacular/stateless_actions/chef_bootstrap.rb +40 -0
- data/lib/cheftacular/stateless_actions/chef_environment.rb +21 -0
- data/lib/cheftacular/stateless_actions/clean_cookbooks.rb +104 -0
- data/lib/cheftacular/stateless_actions/clean_sensu_plugins.rb +19 -0
- data/lib/cheftacular/stateless_actions/clean_server_passwords.rb +14 -0
- data/lib/cheftacular/stateless_actions/cleanup_log_files.rb +14 -0
- data/lib/cheftacular/stateless_actions/client_list.rb +89 -0
- data/lib/cheftacular/stateless_actions/cloud.rb +107 -0
- data/lib/cheftacular/stateless_actions/cloud_bootstrap.rb +109 -0
- data/lib/cheftacular/stateless_actions/compile_audit_log.rb +60 -0
- data/lib/cheftacular/stateless_actions/compile_readme.rb +41 -0
- data/lib/cheftacular/stateless_actions/create_git_key.rb +67 -0
- data/lib/cheftacular/stateless_actions/disk_report.rb +75 -0
- data/lib/cheftacular/stateless_actions/environment.rb +100 -0
- data/lib/cheftacular/stateless_actions/fetch_file.rb +24 -0
- data/lib/cheftacular/stateless_actions/fix_known_hosts.rb +70 -0
- data/lib/cheftacular/stateless_actions/full_bootstrap.rb +30 -0
- data/lib/cheftacular/stateless_actions/get_active_ssh_connections.rb +18 -0
- data/lib/cheftacular/stateless_actions/get_haproxy_log.rb +55 -0
- data/lib/cheftacular/stateless_actions/get_log_from_bag.rb +38 -0
- data/lib/cheftacular/stateless_actions/get_pg_pass.rb +61 -0
- data/lib/cheftacular/stateless_actions/help.rb +71 -0
- data/lib/cheftacular/stateless_actions/initialize_data_bag_contents.rb +220 -0
- data/lib/cheftacular/stateless_actions/knife_upload.rb +23 -0
- data/lib/cheftacular/stateless_actions/pass.rb +49 -0
- data/lib/cheftacular/stateless_actions/reinitialize.rb +46 -0
- data/lib/cheftacular/stateless_actions/remove_client.rb +81 -0
- data/lib/cheftacular/stateless_actions/replication_status.rb +103 -0
- data/lib/cheftacular/stateless_actions/restart_swap.rb +55 -0
- data/lib/cheftacular/stateless_actions/rvm.rb +14 -0
- data/lib/cheftacular/stateless_actions/server_update.rb +99 -0
- data/lib/cheftacular/stateless_actions/service.rb +14 -0
- data/lib/cheftacular/stateless_actions/test_env.rb +82 -0
- data/lib/cheftacular/stateless_actions/update_split_branches.rb +64 -0
- data/lib/cheftacular/stateless_actions/update_tld.rb +62 -0
- data/lib/cheftacular/stateless_actions/upload_nodes.rb +120 -0
- data/lib/cheftacular/stateless_actions/upload_roles.rb +24 -0
- data/lib/cheftacular/version.rb +5 -0
- data/lib/cheftacular.rb +4 -0
- data/lib/cloud_interactor/authentication.rb +56 -0
- data/lib/cloud_interactor/cloud_interactor.rb +23 -0
- data/lib/cloud_interactor/domain/create.rb +17 -0
- data/lib/cloud_interactor/domain/create_record.rb +27 -0
- data/lib/cloud_interactor/domain/destroy.rb +17 -0
- data/lib/cloud_interactor/domain/destroy_record.rb +23 -0
- data/lib/cloud_interactor/domain/list.rb +9 -0
- data/lib/cloud_interactor/domain/list_records.rb +22 -0
- data/lib/cloud_interactor/domain/read.rb +23 -0
- data/lib/cloud_interactor/domain/read_record.rb +27 -0
- data/lib/cloud_interactor/domain/update.rb +18 -0
- data/lib/cloud_interactor/domain/update_record.rb +42 -0
- data/lib/cloud_interactor/domain.rb +18 -0
- data/lib/cloud_interactor/flavor.rb +27 -0
- data/lib/cloud_interactor/helpers.rb +70 -0
- data/lib/cloud_interactor/image.rb +27 -0
- data/lib/cloud_interactor/parser.rb +37 -0
- data/lib/cloud_interactor/server/attach_volume.rb +33 -0
- data/lib/cloud_interactor/server/create.rb +39 -0
- data/lib/cloud_interactor/server/destroy.rb +11 -0
- data/lib/cloud_interactor/server/detach_volume.rb +21 -0
- data/lib/cloud_interactor/server/list.rb +7 -0
- data/lib/cloud_interactor/server/list_volumes.rb +25 -0
- data/lib/cloud_interactor/server/poll.rb +22 -0
- data/lib/cloud_interactor/server/read.rb +9 -0
- data/lib/cloud_interactor/server/read_volume.rb +24 -0
- data/lib/cloud_interactor/server.rb +17 -0
- data/lib/cloud_interactor/version.rb +4 -0
- data/lib/cloud_interactor/volume/create.rb +13 -0
- data/lib/cloud_interactor/volume/destroy.rb +11 -0
- data/lib/cloud_interactor/volume/list.rb +7 -0
- data/lib/cloud_interactor/volume/read.rb +9 -0
- data/lib/cloud_interactor/volume.rb +20 -0
- data/lib/ridley/monkeypatches.rb +11 -0
- data/lib/sshkit/actions/start_commit_check.rb +19 -0
- data/lib/sshkit/actions/start_deploy.rb +25 -0
- data/lib/sshkit/actions/start_log_fetch.rb +91 -0
- data/lib/sshkit/actions/start_task.rb +29 -0
- data/lib/sshkit/getters.rb +67 -0
- data/lib/sshkit/helpers.rb +13 -0
- data/lib/sshkit/monkeypatches.rb +19 -0
- metadata +375 -0
|
@@ -0,0 +1,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
|