cheftacular 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|