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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ae0db74e13fa0ee3670b8c81eb6058041e6d3f94
4
- data.tar.gz: 7ceea94eea62ef4763f6e4879713e53c4e86a3f8
3
+ metadata.gz: 10f51fe71394a87e42196c9a3180865a43f652f9
4
+ data.tar.gz: 8ab142943a10fd6f16a9b75519fc5f5dcc7a9633
5
5
  SHA512:
6
- metadata.gz: 6865a593f1446c1af501ac563dd4d6f4bd180d577e682003ef5b0bac448e4e987c952480f6d832ae485a875d82f992abbd08465f73df5220ec35fd3913110efe
7
- data.tar.gz: 53999bf44b07c5d621998fb53732e76b5c17700a33368d2fff2a4655c68eeb973f2597f95826d092d14dda7b3dccac1a302b2047d81a15b7314ad8bfae81a0e9
6
+ metadata.gz: 1c66d87535954bfc01de4ee4b68b54559fbeb83a206d244fa3e3cf7d80600bf53003c6661b2d3f1f9c5cf7d5a26b38831c0ecd966cab7a0a7607eb674c72be96
7
+ data.tar.gz: 26289288c791fd916fe04cbab2d6e40e77d1c5fab4e2a92ff8f542d03f74d7e0d6f46b31d5e4fce4161a2e87183d40240e53c807dd5b09b694c2258edc8904cc
@@ -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 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.
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
- 31. `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
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
- 32. `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.
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
- 33. `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.
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
- 34. `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.
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
- 35. `cft upload_nodes` This command will resync the chef server's nodes with the data in our chef-repo/node_roles.
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
- 36. `cft upload_roles` This command will resync the chef server's roles with the data in the chef-repo/roles.
390
+ 37. `cft upload_roles` This command will resync the chef server's roles with the data in the chef-repo/roles.
@@ -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.update('logs', description: "store logs for an environment")
38
+ item.attributes = {id: 'logs', description: "store logs for an environment"}
39
39
 
40
- @config[env]['logs_bag_hash'] = @config[env]['logs_bag'].to_hash
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
 
@@ -20,6 +20,7 @@ require 'socket'
20
20
  require 'net/http'
21
21
  require 'timeout'
22
22
  require 'slack-notifier'
23
+ require 'cloudflare'
23
24
 
24
25
  Dir["#{File.dirname(__FILE__)}/../**/*.rb"].each { |f| require f }
25
26
 
@@ -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.pem' : @config['cheftacular']['cheftacular_chef_user'] }.pem"),
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
  )
@@ -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
- parse_repository(working_dir)
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'] = working_dir
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
- #TODO
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['getters'].get_split_branch_hash
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
 
@@ -59,4 +59,4 @@ class Cheftacular
59
59
  @config['ChefDataBag'].save_addresses_bag
60
60
  end
61
61
  end
62
- end
62
+ end
@@ -1,5 +1,5 @@
1
1
  class Cheftacular
2
2
  #major_version.minor_version.bugfixes
3
- VERSION = "2.1.1"
3
+ VERSION = "2.1.2"
4
4
  RUBY_VERSION = "2.2.2"
5
5
  end
@@ -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.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-27 00:00:00.000000000 Z
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