cheftacular 2.1.1 → 2.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/cheftacular/README.md +15 -7
- data/lib/cheftacular/auditor.rb +0 -22
- data/lib/cheftacular/chef/data_bag.rb +4 -2
- data/lib/cheftacular/cheftacular.rb +1 -0
- data/lib/cheftacular/helpers.rb +41 -0
- data/lib/cheftacular/initializers.rb +2 -2
- data/lib/cheftacular/parsers.rb +2 -2
- data/lib/cheftacular/stateless_actions/compile_audit_log.rb +0 -1
- data/lib/cheftacular/stateless_actions/service.rb +72 -1
- data/lib/cheftacular/stateless_actions/update_cloudflare.rb +67 -0
- data/lib/cheftacular/stateless_actions/update_split_branches.rb +1 -1
- data/lib/cheftacular/stateless_actions/update_tld.rb +1 -1
- data/lib/cheftacular/version.rb +1 -1
- data/lib/sshkit/helpers.rb +4 -0
- metadata +17 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 10f51fe71394a87e42196c9a3180865a43f652f9
|
4
|
+
data.tar.gz: 8ab142943a10fd6f16a9b75519fc5f5dcc7a9633
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1c66d87535954bfc01de4ee4b68b54559fbeb83a206d244fa3e3cf7d80600bf53003c6661b2d3f1f9c5cf7d5a26b38831c0ecd966cab7a0a7607eb674c72be96
|
7
|
+
data.tar.gz: 26289288c791fd916fe04cbab2d6e40e77d1c5fab4e2a92ff8f542d03f74d7e0d6f46b31d5e4fce4161a2e87183d40240e53c807dd5b09b694c2258edc8904cc
|
data/lib/cheftacular/README.md
CHANGED
@@ -341,9 +341,17 @@
|
|
341
341
|
|
342
342
|
1. `hip apt_update restart` will prompt to ask if you also want to restart all servers in a rolling restart. This should be done with extreme caution and only in a worst-case scenario.
|
343
343
|
|
344
|
-
30. `cft
|
344
|
+
30. `cft service [COMMAND] [SERVICE]` will run service commands on remote servers. This command only runs on the first server it comes across. Specify others with -n NODE_NAME.
|
345
345
|
|
346
|
-
|
346
|
+
1. When no commands are passed, the command will list all the services in the /etc/init directory
|
347
|
+
|
348
|
+
2. When `list` is passed, the above behavior is performed
|
349
|
+
|
350
|
+
3. When `restart|stop|start SERVICE` is passed, the command will attempt to restart|stop|start the service if it has a .conf file on the remote server in the /etc/init directory.
|
351
|
+
|
352
|
+
31. `cft slack "MESSAGE" [CHANNEL]` will attempt to post the message to the webhook set in your cheftacular.yml. Slack posts to your default channel by default but if the CHANNEL argument is supplied the message will post there.
|
353
|
+
|
354
|
+
32. `cft test_env [TARGET_ENV] boot|destroy` will create (or destroy) the test nodes for a particular environment (defaults to staging, prod split-envs can be set with `-p`). Please read below for how TARGET_ENV works
|
347
355
|
|
348
356
|
1. TARGET_ENV changes functionality depending on the overall (like staging / production) environment
|
349
357
|
|
@@ -353,9 +361,9 @@
|
|
353
361
|
|
354
362
|
3. The default tld used should change depending on which environment you are booting / destroying. This is set in the environment's config data bag under the tld key
|
355
363
|
|
356
|
-
|
364
|
+
33. `cft ubuntu_bootstrap ADDRESS ROOT_PASS` This command will bring a fresh server to a state where chef-client can be run on it via `cft chef-bootstrap`. It should be noted that it is in this step where a server's randomized deploy_user sudo password is generated.
|
357
365
|
|
358
|
-
|
366
|
+
34. `cft update_split_branches` will perform a series of git commands that will merge all the split branches for your split_branch enabled repositories with what is currently on master and push them.
|
359
367
|
|
360
368
|
1. Repository must be set with `-R REPOSITORY_NAME` for this command to work.
|
361
369
|
|
@@ -365,9 +373,9 @@
|
|
365
373
|
|
366
374
|
4. This command will return a helpful error statement if you attempt to run the command with changes to your current working directory. You must commit these changes before running this command.
|
367
375
|
|
368
|
-
|
376
|
+
35. `cft update_tld TLD` command will force a full dns update for a tld in the preferred cloud. It will ensure all the subdomain entries are correct (based on the contents of the addresses data bag) and update them if they are not. It will also create the local subdomain for the entry as well if it does exist and point it to the correct private address.
|
369
377
|
|
370
|
-
|
378
|
+
36. `cft upload_nodes` This command will resync the chef server's nodes with the data in our chef-repo/node_roles.
|
371
379
|
|
372
380
|
1. This command changes behavior depending on several factors about both your mode and the state of your environment
|
373
381
|
|
@@ -379,4 +387,4 @@
|
|
379
387
|
|
380
388
|
1. Due to this, only users running this against their chef-repo need to worry about having a nodes_dir, the way it should be.
|
381
389
|
|
382
|
-
|
390
|
+
37. `cft upload_roles` This command will resync the chef server's roles with the data in the chef-repo/roles.
|
data/lib/cheftacular/auditor.rb
CHANGED
@@ -30,29 +30,7 @@ class Cheftacular
|
|
30
30
|
end
|
31
31
|
|
32
32
|
def fetch_audit_data_hash ret_hash={}, ip=""
|
33
|
-
begin
|
34
|
-
Timeout::timeout(10) do
|
35
|
-
response = Net::HTTP.get URI.parse('http://checkip.dyndns.org')
|
36
|
-
ip = response.match( /(?:Address: )([\d\.]+)/ )[1]
|
37
|
-
end
|
38
|
-
rescue StandardError => e
|
39
|
-
tries ||= 4
|
40
|
-
|
41
|
-
puts "Unable to fetch remote IP address for auditing hash. Trying #{tries} more times."
|
42
|
-
|
43
|
-
tries -= 1
|
44
|
-
|
45
|
-
if tries > 0
|
46
|
-
sleep(15)
|
47
|
-
|
48
|
-
retry
|
49
|
-
else
|
50
|
-
ip = "Unable to fetch"
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
33
|
ret_hash['hostname'] = Socket.gethostname
|
55
|
-
ret_hash['true_ip'] = ip
|
56
34
|
|
57
35
|
ret_hash
|
58
36
|
rescue StandardError => exception
|
@@ -35,9 +35,11 @@ class Cheftacular
|
|
35
35
|
rescue Ridley::Errors::HTTPRequestEntityTooLarge => e
|
36
36
|
puts "WARNING! #{ e }! The logs from this run will not be saved on the chef server. Wiping the bag so future runs can be saved."
|
37
37
|
|
38
|
-
item.
|
38
|
+
item.attributes = {id: 'logs', description: "store logs for an environment"}
|
39
39
|
|
40
|
-
|
40
|
+
item.save
|
41
|
+
|
42
|
+
@config[env]['logs_bag_hash'] = @config[env]['logs_bag'].reload.to_hash
|
41
43
|
end
|
42
44
|
end
|
43
45
|
|
data/lib/cheftacular/helpers.rb
CHANGED
@@ -275,6 +275,47 @@ class Cheftacular
|
|
275
275
|
end
|
276
276
|
end
|
277
277
|
|
278
|
+
def does_cheftacular_config_have? key_array, *args
|
279
|
+
cheftacular = @config['cheftacular']
|
280
|
+
key_array = [key_array] if key_array.is_a?(String)
|
281
|
+
key_checks = []
|
282
|
+
|
283
|
+
key_array.each do |key|
|
284
|
+
key_checks << recursive_hash_check(key.split(':'), @config['cheftacular']).to_s
|
285
|
+
end
|
286
|
+
|
287
|
+
!key_checks.include?('false')
|
288
|
+
end
|
289
|
+
|
290
|
+
def recursive_hash_check keys, hash
|
291
|
+
if hash.has_key?(keys[0])
|
292
|
+
case hash[keys[0]].class
|
293
|
+
when Hash
|
294
|
+
if !hash[keys[0]].empty?
|
295
|
+
recursive_hash_check keys[1..keys.count-1], hash[keys[0]]
|
296
|
+
else
|
297
|
+
return true
|
298
|
+
end
|
299
|
+
when String
|
300
|
+
return !hash[keys[0]].blank?
|
301
|
+
when Array
|
302
|
+
return !hash[keys[0]].empty?
|
303
|
+
end
|
304
|
+
else
|
305
|
+
return false
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
def parse_node_name_from_client_file ret=""
|
310
|
+
config = File.read(File.expand_path("#{ @config['locs']['chef'] }/client.rb"))
|
311
|
+
|
312
|
+
config.split("\n").each do |line|
|
313
|
+
next unless line.include?('node_name')
|
314
|
+
|
315
|
+
return line.split('node_name').last.strip.chomp.gsub('"', '')
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
278
319
|
private
|
279
320
|
def current_file_path file_name
|
280
321
|
File.join( @config['locs']['app-root'], 'tmp', declassify, "#{ Time.now.strftime("%Y%m%d") }-#{ file_name }")
|
@@ -296,8 +296,8 @@ class Cheftacular
|
|
296
296
|
|
297
297
|
@config['ridley'] = Ridley.new(
|
298
298
|
server_url: @config['cheftacular']['chef_server_url'],
|
299
|
-
client_name: (@config['helper'].running_on_chef_node? ? parse_node_name_from_client_file : @config['cheftacular']['cheftacular_chef_user']),
|
300
|
-
client_key: File.expand_path("#{ @config['locs']['chef'] }/#{ @config['helper'].running_on_chef_node? ? 'client
|
299
|
+
client_name: (@config['helper'].running_on_chef_node? ? @config['parser'].parse_node_name_from_client_file : @config['cheftacular']['cheftacular_chef_user']),
|
300
|
+
client_key: File.expand_path("#{ @config['locs']['chef'] }/#{ @config['helper'].running_on_chef_node? ? 'client' : @config['cheftacular']['cheftacular_chef_user'] }.pem"),
|
301
301
|
encrypted_data_bag_secret: @config['data_bag_secret'],
|
302
302
|
ssl: { verify: @config['cheftacular']['ssl_verify'] == 'true' }
|
303
303
|
)
|
data/lib/cheftacular/parsers.rb
CHANGED
@@ -38,9 +38,9 @@ class Cheftacular
|
|
38
38
|
end
|
39
39
|
|
40
40
|
if @config['getter'].get_repository_from_role_name(working_dir, "has_value?")
|
41
|
-
|
41
|
+
@options['repository'] = working_dir unless @options['repository'] #enable custom -r or -R flags to get through in application directories
|
42
42
|
|
43
|
-
@options['repository']
|
43
|
+
parse_repository(@options['repository'])
|
44
44
|
|
45
45
|
@options['command'] = ARGV[0] unless @config['helper'].is_not_command_or_stateless_command?(ARGV[0])
|
46
46
|
end
|
@@ -41,7 +41,6 @@ class Cheftacular
|
|
41
41
|
log_arr.each do |log_hash|
|
42
42
|
out << " #{ log_array_entry_count }. #{ log_hash['command'] }"
|
43
43
|
out << " 1. Hostname: #{ log_hash['hostname'] }"
|
44
|
-
out << " 2. IPAddress: #{ log_hash['true_ip'] }"
|
45
44
|
out << " 3. Arguments: #{ log_hash['arguments'] }"
|
46
45
|
out << " 4. Options: #{ log_hash['options'].to_hash }"
|
47
46
|
out << ""
|
@@ -2,13 +2,84 @@
|
|
2
2
|
class Cheftacular
|
3
3
|
class StatelessActionDocumentation
|
4
4
|
def service
|
5
|
+
@config['documentation']['stateless_action'] << [
|
6
|
+
"`cft service [COMMAND] [SERVICE]` will run service commands on remote servers. " +
|
7
|
+
"This command only runs on the first server it comes across. Specify others with -n NODE_NAME.",
|
5
8
|
|
9
|
+
[
|
10
|
+
" 1. When no commands are passed, the command will list all the services in the /etc/init directory",
|
11
|
+
|
12
|
+
" 2. When `list` is passed, the above behavior is performed ",
|
13
|
+
|
14
|
+
" 3. When `restart|stop|start SERVICE` is passed, the command will attempt to restart|stop|start the " +
|
15
|
+
"service if it has a .conf file on the remote server in the /etc/init directory."
|
16
|
+
]
|
17
|
+
]
|
6
18
|
end
|
7
19
|
end
|
8
20
|
|
9
21
|
class StatelessAction
|
10
22
|
def service
|
11
|
-
|
23
|
+
raise "This action can only be performed if the mode is set to devops" unless @config['helper'].running_in_mode?('devops')
|
24
|
+
|
25
|
+
command = case ARGV[1]
|
26
|
+
when nil then 'list'
|
27
|
+
when /list/ then 'list'
|
28
|
+
when 'restart' then "#{ ARGV[2] } restart"
|
29
|
+
when 'stop' then "#{ ARGV[2] } stop"
|
30
|
+
when 'start' then "#{ ARGV[2] } start"
|
31
|
+
else 'list'
|
32
|
+
end
|
33
|
+
|
34
|
+
raise "You did not pass a service to #{ ARGV[1] }" if ARGV[1] =~ /restart|stop|start/ && ARGV[2].nil?
|
35
|
+
|
36
|
+
service_location = "#{ ARGV[2] }.conf"
|
37
|
+
|
38
|
+
nodes = @config['getter'].get_true_node_objects
|
39
|
+
|
40
|
+
nodes = @config['parser'].exclude_nodes( nodes, [{ unless: { env: @options['env'] }}], true )
|
41
|
+
|
42
|
+
options, locs, ridley, logs_bag_hash, pass_bag_hash, bundle_command, cheftacular, passwords = @config['helper'].set_local_instance_vars
|
43
|
+
|
44
|
+
on ( nodes.map { |n| "deploy@" + n.public_ipaddress } ) do |host|
|
45
|
+
n = get_node_from_address(nodes, host.hostname)
|
46
|
+
|
47
|
+
puts "Beginning run of \"service #{ command }\" for #{ n.name } (#{ n.public_ipaddress })"
|
48
|
+
|
49
|
+
start_service_run( n.name, n.public_ipaddress, options, locs, passwords, command, cheftacular, service_location )
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
module SSHKit
|
56
|
+
module Backend
|
57
|
+
class Netssh
|
58
|
+
def start_service_run name, ip_address, options, locs, passwords, command, cheftacular, service_location, out=""
|
59
|
+
log_loc, timestamp = set_log_loc_and_timestamp(locs)
|
60
|
+
run_list_command = command == 'list'
|
61
|
+
|
62
|
+
#puts "Generating service run log file for #{ name } (#{ ip_address }) at #{ log_loc }/rolelog/#{ name }-service-#{ timestamp }.txt"
|
63
|
+
|
64
|
+
#TODO check other locations for services!
|
65
|
+
if !run_list_command && !sudo_test( passwords[ip_address], "/etc/init/#{ service_location }" ) #true if file exists
|
66
|
+
puts "#{ name } (#{ ip_address }) cannot run #{ command } as it's .conf file does not exist! Running list instead..."
|
67
|
+
|
68
|
+
run_list_command = true
|
69
|
+
end
|
70
|
+
|
71
|
+
if run_list_command
|
72
|
+
out << capture( :ls, '-al', '/etc/init' )
|
73
|
+
else
|
74
|
+
out << sudo_capture( passwords[ip_address], 'service', command)
|
75
|
+
end
|
76
|
+
|
77
|
+
#::File.open("#{ log_loc }/rolelog/#{ name }-service-#{ timestamp }.txt", "w") { |f| f.write(out.scrub_pretty_text) } unless options['no_logs']
|
78
|
+
|
79
|
+
puts out.scrub_pretty_text
|
80
|
+
|
81
|
+
puts "Succeeded run of \"service #{ command }\" for #{ name } (#{ ip_address })"
|
82
|
+
end
|
12
83
|
end
|
13
84
|
end
|
14
85
|
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
class Cheftacular
|
2
|
+
class StatelessActionDocumentation
|
3
|
+
def update_cloudflare
|
4
|
+
@config['documentation']['stateless_action'] << [
|
5
|
+
"`cft update_cloudflare` command will force a full dns update for clouflare. " +
|
6
|
+
"It will ensure all the subdomain entries are correct (based on the contents of the addresses data bag) " +
|
7
|
+
"and update them if they are not. It will also create the local subdomain for the entry as well if it " +
|
8
|
+
"does exist and point it to the correct private address for an environment."
|
9
|
+
]
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class StatelessAction
|
14
|
+
def update_cloudflare
|
15
|
+
raise "This action can only be performed if the mode is set to devops" unless @config['helper'].running_in_mode?('devops')
|
16
|
+
|
17
|
+
nodes = @config['getter'].get_true_node_objects(true)
|
18
|
+
|
19
|
+
nodes = @config['parser'].exclude_nodes( nodes, [{ unless: { env: @options['env'] }}] )
|
20
|
+
|
21
|
+
addr_data = @config['getter'].get_addresses_hash @options['env']
|
22
|
+
|
23
|
+
unless @config['helper'].does_cheftacular_config_have?(['cloudflare_api_key', 'cloudflare_user_email'])
|
24
|
+
puts "Critical! You tried to run #{ __method__ } but have not set a cloudflare_api_key or cloudflare_user_email! Please set these keys and run this method again!"
|
25
|
+
|
26
|
+
exit
|
27
|
+
end
|
28
|
+
|
29
|
+
exit
|
30
|
+
|
31
|
+
cloudflare = CloudFlare::connection(@config['cheftacular']['cloudflare_api_key'], @config['cheftacular']['cloudflare_user_email'])
|
32
|
+
|
33
|
+
nodes.each do |n|
|
34
|
+
|
35
|
+
@options['node_name'] = n.name
|
36
|
+
|
37
|
+
domain_obj = PublicSuffix.parse addr_data[n.public_ipaddress]['dn']
|
38
|
+
|
39
|
+
next unless domain_obj.domain == @config[@options['env']]['config_bag_hash'][@options['sub_env']]['tld'] #we can't create records for domains we dont manage in rax
|
40
|
+
|
41
|
+
@config['stateless_action'].cloud "domain", "create:#{ tld }:#{ domain_obj.trd }:#{ n.public_ipaddress }"
|
42
|
+
|
43
|
+
sleep 5 #don't want to to push updates to rax too fast
|
44
|
+
|
45
|
+
@config['stateless_action'].cloud "domain", "create:#{ tld }:local.#{ domain_obj.trd }:#{ addr_data[n.public_ipaddress]['priv'] }"
|
46
|
+
|
47
|
+
full_domain = "#{ domain_obj.trd }.#{ tld }"
|
48
|
+
|
49
|
+
target_serv_index = @config[@options['env']]['addresses_bag_hash']['addresses'].count
|
50
|
+
|
51
|
+
@config[@options['env']]['addresses_bag_hash']['addresses'].each do |serv_hash|
|
52
|
+
target_serv_index = @config[@options['env']]['addresses_bag_hash']['addresses'].index(serv_hash) if serv_hash['name'] == n.name
|
53
|
+
end
|
54
|
+
|
55
|
+
@config[@options['env']]['addresses_bag_hash']['addresses'][target_serv_hash] ||= {}
|
56
|
+
@config[@options['env']]['addresses_bag_hash']['addresses'][target_serv_hash]['dn'] = full_domain
|
57
|
+
|
58
|
+
sleep 5 #prepare for next domain
|
59
|
+
end
|
60
|
+
|
61
|
+
@config[@options['env']]['config_bag_hash'][@options['sub_env']]['tld'] = tld
|
62
|
+
|
63
|
+
@config['ChefDataBag'].save_config_bag
|
64
|
+
@config['ChefDataBag'].save_addresses_bag
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -29,7 +29,7 @@ class Cheftacular
|
|
29
29
|
|
30
30
|
puts "Preparing to run merges..."
|
31
31
|
|
32
|
-
split_branch_repos = @config['
|
32
|
+
split_branch_repos = @config['getter'].get_split_branch_hash
|
33
33
|
|
34
34
|
raise "unsupported codebase, please run in #{ split_branch_repos.keys.join(', ') } only!" if ( @options['repository'] =~ /#{ split_branch_repos.keys.join('|') }/ ) == 0
|
35
35
|
|
data/lib/cheftacular/version.rb
CHANGED
data/lib/sshkit/helpers.rb
CHANGED
@@ -9,6 +9,10 @@ module SSHKit
|
|
9
9
|
capture :echo, pass, :|, :sudo, '-S', *args
|
10
10
|
end
|
11
11
|
|
12
|
+
def sudo_test pass, file_location
|
13
|
+
sudo_capture( pass, :test, '-e', file_location, '&&', :echo, 'true', { raise_on_non_zero_exit: false, verbosity: Logger::DEBUG }) == 'true'
|
14
|
+
end
|
15
|
+
|
12
16
|
def has_run_list_in_role_map? run_list, role_map_hash
|
13
17
|
role_map_hash.each_value do |map_hash|
|
14
18
|
return true if run_list.include?("role[#{ map_hash['role_name'] }]")
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cheftacular
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.1.
|
4
|
+
version: 2.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Louis Alridge
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-04-
|
11
|
+
date: 2015-04-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: hashie
|
@@ -206,6 +206,20 @@ dependencies:
|
|
206
206
|
- - ">="
|
207
207
|
- !ruby/object:Gem::Version
|
208
208
|
version: '0'
|
209
|
+
- !ruby/object:Gem::Dependency
|
210
|
+
name: cloudflare
|
211
|
+
requirement: !ruby/object:Gem::Requirement
|
212
|
+
requirements:
|
213
|
+
- - ">="
|
214
|
+
- !ruby/object:Gem::Version
|
215
|
+
version: '0'
|
216
|
+
type: :runtime
|
217
|
+
prerelease: false
|
218
|
+
version_requirements: !ruby/object:Gem::Requirement
|
219
|
+
requirements:
|
220
|
+
- - ">="
|
221
|
+
- !ruby/object:Gem::Version
|
222
|
+
version: '0'
|
209
223
|
description: Ruby gem for managing a chef stack. Primarily targetted towards rails
|
210
224
|
stacks and is designed to be easy to use like Heroku CLI
|
211
225
|
email:
|
@@ -291,6 +305,7 @@ files:
|
|
291
305
|
- lib/cheftacular/stateless_actions/service.rb
|
292
306
|
- lib/cheftacular/stateless_actions/slack.rb
|
293
307
|
- lib/cheftacular/stateless_actions/test_env.rb
|
308
|
+
- lib/cheftacular/stateless_actions/update_cloudflare.rb
|
294
309
|
- lib/cheftacular/stateless_actions/update_split_branches.rb
|
295
310
|
- lib/cheftacular/stateless_actions/update_tld.rb
|
296
311
|
- lib/cheftacular/stateless_actions/upload_nodes.rb
|