Class: Cheftacular::StatelessAction

Inherits:
Object
  • Object
show all
Includes:
RbConfig
Defined in:
lib/cheftacular/stateless_action.rb,
lib/cheftacular/stateless_actions/rvm.rb,
lib/cheftacular/stateless_actions/help.rb,
lib/cheftacular/stateless_actions/pass.rb,
lib/cheftacular/stateless_actions/cloud.rb,
lib/cheftacular/stateless_actions/slack.rb,
lib/cheftacular/stateless_actions/backups.rb,
lib/cheftacular/stateless_actions/service.rb,
lib/cheftacular/stateless_actions/test_env.rb,
lib/cheftacular/stateless_actions/arguments.rb,
lib/cheftacular/stateless_actions/fetch_file.rb,
lib/cheftacular/stateless_actions/update_tld.rb,
lib/cheftacular/stateless_actions/client_list.rb,
lib/cheftacular/stateless_actions/disk_report.rb,
lib/cheftacular/stateless_actions/environment.rb,
lib/cheftacular/stateless_actions/get_pg_pass.rb,
lib/cheftacular/stateless_actions/knife_upload.rb,
lib/cheftacular/stateless_actions/reinitialize.rb,
lib/cheftacular/stateless_actions/restart_swap.rb,
lib/cheftacular/stateless_actions/upload_nodes.rb,
lib/cheftacular/stateless_actions/upload_roles.rb,
lib/cheftacular/stateless_actions/remove_client.rb,
lib/cheftacular/stateless_actions/server_update.rb,
lib/cheftacular/stateless_actions/chef_bootstrap.rb,
lib/cheftacular/stateless_actions/compile_readme.rb,
lib/cheftacular/stateless_actions/create_git_key.rb,
lib/cheftacular/stateless_actions/full_bootstrap.rb,
lib/cheftacular/stateless_actions/clean_cookbooks.rb,
lib/cheftacular/stateless_actions/cloud_bootstrap.rb,
lib/cheftacular/stateless_actions/fix_known_hosts.rb,
lib/cheftacular/stateless_actions/get_haproxy_log.rb,
lib/cheftacular/stateless_actions/chef_environment.rb,
lib/cheftacular/stateless_actions/get_log_from_bag.rb,
lib/cheftacular/stateless_actions/cleanup_log_files.rb,
lib/cheftacular/stateless_actions/compile_audit_log.rb,
lib/cheftacular/stateless_actions/add_ssh_key_to_bag.rb,
lib/cheftacular/stateless_actions/replication_status.rb,
lib/cheftacular/stateless_actions/clean_sensu_plugins.rb,
lib/cheftacular/stateless_actions/update_split_branches.rb,
lib/cheftacular/stateless_actions/clean_server_passwords.rb,
lib/cheftacular/stateless_actions/check_cheftacular_settings.rb,
lib/cheftacular/stateless_actions/get_active_ssh_connections.rb,
lib/cheftacular/stateless_actions/initialize_data_bag_contents.rb,
lib/cheftacular/stateless_actions/bootstrappers/centos_bootstrap.rb,
lib/cheftacular/stateless_actions/bootstrappers/coreos_bootstrap.rb,
lib/cheftacular/stateless_actions/bootstrappers/fedora_bootstrap.rb,
lib/cheftacular/stateless_actions/bootstrappers/redhat_bootstrap.rb,
lib/cheftacular/stateless_actions/bootstrappers/ubuntu_bootstrap.rb,
lib/cheftacular/stateless_actions/bootstrappers/vyatta_bootstrap.rb,
lib/cheftacular/stateless_actions/update_cloudflare_dns_from_cloud.rb,
lib/cheftacular/stateless_actions/get_shorewall_allowed_connections.rb

Instance Method Summary collapse

Constructor Details

#initialize(options, config) ⇒ StatelessAction

Returns a new instance of StatelessAction.



12
13
14
# File 'lib/cheftacular/stateless_action.rb', line 12

def initialize options, config
  @options, @config = options, config
end

Instance Method Details

#add_ssh_key_to_bag(specific_repository = "") ⇒ Object

TODO key for environment specific deploys?



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/cheftacular/stateless_actions/add_ssh_key_to_bag.rb', line 20

def add_ssh_key_to_bag specific_repository=""
  raise "This action can only be performed if the mode is set to devops" unless @config['helper'].running_in_mode?('devops')
  
  raise "Please put quotes around your SSH public key!" if ARGV[1].length < 25 #TODO REFACTOR to accurate length of shortest key

  specific_repository = ARGV[2] if ARGV[2] && specific_repository.empty?
  
  if !specific_repository.empty? && @config['getter'].get_repo_names_for_repositories.include?(specific_repository)
    puts "The repository passed (#{ specific_repository }) is not listed in the cheftacular.yml repositories hash! Please update the hash or check your spelling!"

    return false
  end

  public_ssh_key = ARGV[1]

  if specific_repository.blank?
    @config['default']['authentication_bag_hash']["authorized_keys"] << public_ssh_key
  else
    @config['default']['authentication_bag_hash']["specific_authorized_keys"] << public_ssh_key
  end

  @config['ChefDataBag'].save_authentication_bag
end

#argumentsObject



59
60
61
62
# File 'lib/cheftacular/stateless_actions/arguments.rb', line 59

def arguments
  #TODO point this command to it's help and display?
  raise "This command currently does nothing, do not try to run it."
end

#backups(force_fetch_and_restore = false) ⇒ Object



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/cheftacular/stateless_actions/backups.rb', line 16

def backups force_fetch_and_restore=false
  fetch_backup   = @config[@options['env']]['config_bag_hash'][@options['sub_env']]['fetch_backups']
  restore_backup = @config[@options['env']]['config_bag_hash'][@options['sub_env']]['restore_backups']

  puts "For #{ @options['env'] } (sub-env: #{ @options['sub_env'] }) fetch backups was set to #{ fetch_backup ? 'on' : 'off' } and restoring backups was set to #{ restore_backup ? 'on' : 'off' }"

  case ARGV[1]
  when 'activate'   then restore_backup, fetch_backup = true, true
  when 'deactivate' then restore_backup, fetch_backup = false, false
  end

  puts "For #{ @options['env'] } (sub-env: #{ @options['sub_env'] }) fetch backups is now set to #{ fetch_backup ? 'on' : 'off' } and restoring backups is now set to #{ restore_backup ? 'on' : 'off' }"

  @config[@options['env']]['config_bag_hash'][@options['sub_env']]['fetch_backups']   = fetch_backup
  @config[@options['env']]['config_bag_hash'][@options['sub_env']]['restore_backups'] = restore_backup

  @config['ChefDataBag'].save_config_bag

  if force_fetch_and_restore
    nodes = @config['getter'].get_true_node_objects true

    db_primary_nodes = @config['parser'].exclude_nodes( nodes, [{ unless: 'role[db_primary]' }, { if: { not_env: 'production' } }])

    backup_slave_local_ip = @config['cheftacular']['backup_server']

    if backup_slave_local_ip == 'first_production_slave'
      backup_slave = @config['parser'].exclude_nodes( nodes, [{ unless: 'role[db_slave]' }, { if: { not_env: 'production' } }], true)

      backup_slave_local_ip = @config['getter'].get_address_hash(backup_slave.first.hostname)['priv']
    end

    options, locs, ridley, logs_bag_hash, pass_bag_hash, bundle_command, cheftacular, passwords = set_local_instance_vars

    on ( db_primary_nodes.map { |n| "deploy@" + n.public_ipaddress } ) do |host|
      n = get_node_from_address(nodes, host.hostname)

      puts("Beginning db fetch_and_restore for #{ n.name } (#{ n.public_ipaddress }) for env #{ options['env'] }") unless options['quiet']

      start_db_fetch_and_restore( n.name, n.public_ipaddress, options, locs, cheftacular, passwords, backup_slave_local_ip)
    end

    @config['action'].migrate
  else
    puts "Triggering deploy on databases to refresh backup setting..."

    @options['role'] = 'db_primary'

    @config['action'].deploy
  end
end

#check_cheftacular_settingsObject



10
11
12
# File 'lib/cheftacular/stateless_actions/check_cheftacular_settings.rb', line 10

def check_cheftacular_settings
  raise "Not Yet Implemented"
end

#chef_bootstrap(out = []) ⇒ Object



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/cheftacular/stateless_actions/chef_bootstrap.rb', line 13

def chef_bootstrap out=[]
  raise "This action can only be performed if the mode is set to devops" if !@config['helper'].running_in_mode?('devops') && !@options['in_scaling']
    
  @options['address'] = ARGV[1] unless @options['address']
  @options['node_name'] = ARGV[2] unless @options['node_name']

  @config['stateless_action'].remove_client #just in case

  puts("Starting chef-client initialization...") unless @options['quiet']

  out << `#{ @config['helper'].knife_bootstrap_command }`

  puts(out.last) unless @options['quiet'] || @options['in_scaling']

  puts("Sending up data_bag_key file...") unless @options['quiet']

  out << `scp -oStrictHostKeyChecking=no #{ @config['locs']['chef'] }/#{ @config['cheftacular']['data_bag_key_file'] } #{ @config['cheftacular']['deploy_user'] }@#{ @options['address'] }:/home/#{ @config['cheftacular']['deploy_user'] }`

  puts("Moving key file to chef directory on server...") unless @options['quiet']

  out << `ssh -t -oStrictHostKeyChecking=no #{ @config['cheftacular']['deploy_user'] }@#{ @options['address'] } "#{ @config['helper'].sudo(@options['address']) } mv -f /home/#{ @config['cheftacular']['deploy_user'] }/#{  @config['cheftacular']['data_bag_key_file'] } /etc/chef"`

  @options['force_yes'] = true # have the upload_nodes grab the new nodes

  @config['stateless_action'].upload_nodes
end

#chef_environmentObject



17
18
19
# File 'lib/cheftacular/stateless_actions/chef_environment.rb', line 17

def chef_environment
  raise "Not yet Implemented"
end

#clean_cookbooks(local_options = {'interactive' => true}) ⇒ Object



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/cheftacular/stateless_actions/clean_cookbooks.rb', line 19

def clean_cookbooks local_options={'interactive' => true}
  raise "This action can only be performed if the mode is set to devops" unless @config['helper'].running_in_mode?('devops')

  ARGV.each do |arg|
    case arg
    when "force" then local_options['interactive'] = false
    end
  end

  @config['cheftacular']['wrapper_cookbooks'].split(',').each do |wrapper_cookbook|
    wrapper_cookbook_loc = "#{ @config['locs']['cookbooks'] }/#{ wrapper_cookbook }"
    FileUtils.rm(File.expand_path("#{ wrapper_cookbook_loc }/Berksfile.lock")) if File.exists?(File.expand_path("#{ wrapper_cookbook_loc }/Berksfile.lock"))
    FileUtils.rm_rf(File.expand_path("#{ @config['locs']['berks'] }/cookbooks")) if File.exists?(File.expand_path("#{ @config['locs']['berks'] }/cookbooks"))
    
    Dir.chdir wrapper_cookbook_loc
    puts "Installing new cookbooks..."
    out = `berks install`
    puts "#{out}\nFinished... Beginning directory scanning and conflict resolution..."

    berkshelf_cookbooks = {}

    Dir.foreach(@config['locs']['berks']) do |berkshelf_cookbook|
      next if @config['helper'].is_junk_filename?(berkshelf_cookbook)
      skip = false

      berkshelf_cookbooks.keys.each do |processed_berkshelf_cookbook|
        if processed_berkshelf_cookbook.rpartition('-').first == berkshelf_cookbook.rpartition('-').first
          cookbook_mtime  = File.mtime(File.expand_path("#{ @config['locs']['berks'] }/#{ berkshelf_cookbook }"))
          pcookbook_mtime = File.mtime(File.expand_path("#{ @config['locs']['berks'] }/#{ processed_berkshelf_cookbook }"))

          skip = true if cookbook_mtime < pcookbook_mtime #get only the latest version, berkshelf pulls in multiple commits from git repos for SOME REASON
        end
      end

      next if skip

      berkshelf_cookbooks[berkshelf_cookbook] = if File.exists?(File.expand_path("#{ @config['locs']['berks'] }/#{ berkshelf_cookbook }/metadata.rb"))
                                                  File.read(File.expand_path("#{ @config['locs']['berks'] }/#{ berkshelf_cookbook }/metadata.rb")).gsub('"',"'").gsub(/^version[\s]*('\d[.\d]+')/).peek[/('\d[.\d]+')/].gsub("'",'')
                                                else
                                                  berkshelf_cookbook.split('-').last
                                                end
    end

    chef_repo_cookbooks = {}

    Dir.foreach(@config['locs']['cookbooks']) do |chef_repo_cookbook|
      next if @config['helper'].is_junk_filename?(chef_repo_cookbook)
        
      new_name = chef_repo_cookbook.rpartition('-').first

      chef_repo_cookbooks[chef_repo_cookbook] = if File.exists?(File.expand_path("#{ @config['locs']['cookbooks'] }/#{ chef_repo_cookbook }/metadata.rb"))
                                                  File.read(File.expand_path("#{ @config['locs']['cookbooks'] }/#{ chef_repo_cookbook }/metadata.rb")).gsub('"',"'").gsub(/^version[\s]*('\d[.\d]+')/).peek[/('\d[.\d]+')/].gsub("'",'')
                                                else
                                                  JSON.parse(File.read(File.expand_path("#{ @config['locs']['cookbooks'] }/#{ chef_repo_cookbook }/metadata.json"))).to_hash['version']
                                                end
    end

    berkshelf_cookbooks.each_pair do |berkshelf_cookbook, version|
      new_name = berkshelf_cookbook.rpartition('-').first

      if chef_repo_cookbooks.has_key?(new_name) || chef_repo_cookbooks.has_key?(berkshelf_cookbook) #don't overwrite cookbooks without user input
        if local_options['interactive']
          puts "COOKBOOK::~~~~#{ new_name }~~~~::VERSION::~~~~~~~~#{ version } VS #{ chef_repo_cookbooks[new_name] }"
          puts "\nEnter O | o | overwrite to overwrite ~~~~#{ new_name }~~~~ in the chef-repo (THIS SHOULD NOT BE DONE LIGHTLY)"
          puts "Enter N | n | no        to skip to the next conflict"
          puts "If you pass force to this script, it will always overwrite."
          #puts "If you pass a STRING of comma delimited cookbooks, it will skip these cookbooks automatically and overwrite others"
          #puts "Example: ruby ./executables/clean-cookbooks 'application_ruby,wordpress'"
          puts "Input:"
          input = STDIN.gets.chomp

          next if (input =~ /N|n|no/) == 0
        end

        next if @config['helper'].is_higher_version?(chef_repo_cookbooks[new_name], version)
      end

      cmnd = "#{ @config['locs']['berks'] }/#{ berkshelf_cookbook } #{ @config['locs']['cookbooks'] }/#{ new_name }"
      puts "Moving #{ cmnd } (#{ version }:#{ chef_repo_cookbooks[new_name] })" if @options['verbose']
      `rm -Rf #{ @config['locs']['cookbooks'] }/#{ new_name }`
      `cp -Rf #{ cmnd }`
    end
  end
end

#clean_sensu_pluginsObject



12
13
14
15
16
17
# File 'lib/cheftacular/stateless_actions/clean_sensu_plugins.rb', line 12

def clean_sensu_plugins
  raise "This method is not yet implemented"
  raise "This action can only be performed if the mode is set to devops" unless @config['helper'].running_in_mode?('devops')
  
  #TODO use this to keep the cookbook files directory up to date with the sensu community plugins repo
end

#clean_server_passwordsObject



10
11
12
# File 'lib/cheftacular/stateless_actions/clean_server_passwords.rb', line 10

def clean_server_passwords
  #TODO clean up non-existent entries in all envs server_password bags
end

#cleanup_log_filesObject



10
11
12
# File 'lib/cheftacular/stateless_actions/cleanup_log_files.rb', line 10

def cleanup_log_files
  #TODO
end

#client_listObject



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/cheftacular/stateless_actions/client_list.rb', line 25

def client_list
  @config['helper'].cleanup_file_caches('current')

  nodes = @config['getter'].get_true_node_objects(true)

  @config['chef_environments'].each do |env|
    @config['initializer'].initialize_data_bags_for_environment env, false, ['addresses', 'server_passwords']
  end

  environments = nodes.map { |n| n.chef_environment }

  environments.uniq.each do |env|
    next if env == '_default'

    env_nodes = @config['parser'].exclude_nodes(nodes, [{ if: { not_env: env } }])
    puts "\nFound #{ env_nodes.count } #{ env } nodes:"
    out = "  #{ 'name'.ljust(22) } #{ 'ip_address'.ljust(20) }"
    out << "#{ 'private_address'.ljust(21) }"                   if @options['with_private']
    out << "#{ 'pass?'.ljust(5) } #{ 'domain'.ljust(41) }"      if @options['verbose']
    out << "#{ 'deploy_password'.ljust(21) }"                   if @options['verbose']
    out << "run_list"

    puts out

    auth_hash = @config[env]['server_passwords_bag_hash']

    addresses_hash = @config['getter'].get_addresses_hash env

    env_nodes.each do |node|
      #client = @ridley.client.find(options['node_name'])
      out = "  #{ node.chef_id.ljust(22,'_') }_#{ node.public_ipaddress.ljust(20,'_') }"

      if @options['with_private']
        if addresses_hash.has_key?(node.public_ipaddress)
          out << addresses_hash[node.public_ipaddress]['address'].ljust(20,'_')
        else
          out << ''.ljust(20,'_')
        end
      end

      if @options['verbose']

        out << "_" + auth_hash.has_key?("#{ node.public_ipaddress }-deploy-pass").to_s.ljust(5,'_') + "_"

        if addresses_hash.has_key?(node.public_ipaddress)
          out << addresses_hash[node.public_ipaddress]['dn'].ljust(40,'_')
        else
          out << ''.ljust(40,'_')
        end

        if auth_hash.has_key?("#{ node.public_ipaddress }-deploy-pass")
          out << "_" + auth_hash["#{ node.public_ipaddress }-deploy-pass"]
        else
          out << "_" + ''.ljust(@config['cheftacular']['server_password_length'],'_')
        end
      end

      out << "_#{ node.run_list.join(', ') }"
      
      puts out
    end
  end
end

#cloud(*args) ⇒ Object Also known as: aws, rax



93
94
95
96
97
98
99
100
101
# File 'lib/cheftacular/stateless_actions/cloud.rb', line 93

def cloud *args
  raise "This action can only be performed if the mode is set to devops" if !@config['helper'].running_in_mode?('devops') && !@options['in_scaling']

  args = ARGV[1..ARGV.length] if args.empty?

  @config['cloud_interactor'] ||= CloudInteractor.new(@config['default']['authentication_bag_hash'], @options)

  @config['cloud_interactor'].run args
end

#cloud_bootstrapObject



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/cheftacular/stateless_actions/cloud_bootstrap.rb', line 28

def cloud_bootstrap
  raise "This action can only be performed if the mode is set to devops" if !@config['helper'].running_in_mode?('devops') && !@options['in_scaling']
  
  @options['node_name']   = ARGV[1] unless @options['node_name']
  @options['flavor_name'] = ARGV[2] unless @options['flavor_name']
  @options['descriptor']  = ARGV[3] if ARGV[3] && !@options['descriptor']

  if `which sshpass`.empty?
    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)"
  end

  #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
  @config['stateless_action'].cloud "server", "create:#{ @options['env'] }_#{ @options['node_name'] }:#{ @options['flavor_name'] }"

  status_hash = @config['stateless_action'].cloud "server", "poll:#{ @options['env'] }_#{ @options['node_name'] }"

  status_hash['created_servers'].each do |server_hash|
    next unless server_hash['name'] == "#{ @options['env'] }_#{ @options['node_name'] }"

    @options['address'] = server_hash['ipv4_address']

    @options['private_address'] = server_hash['addresses']['private'][0]['addr']
  end

  begin
    @options['client_pass'] = status_hash['admin_passwords']["#{ @options['env'] }_#{ @options['node_name'] }"]
  rescue NoMethodError => e
    puts "Unable to locate an admin pass for server #{ @options['node_name'] }, does the server already exist? Exiting #{ __method__ }..."

    return false
  end

  tld = @config[@options['env']]['config_bag_hash'][@options['sub_env']]['tld']

  target_serv_index = @config[@options['env']]['addresses_bag_hash']['addresses'].count

  compile_args = ['set_all_attributes']

  compile_args << "set_specific_domain:#{ @options['with_dn'] }" if @options['with_dn']

  address_hash = @config['DNS'].compile_address_hash_for_server_from_options(*compile_args)

  @config['DNS'].create_dns_record_for_domain_from_address_hash(@options['with_dn'], address_hash, "specific_domain_mode") if @options['with_dn']

  @config['DNS'].create_dns_record_for_domain_from_address_hash(tld, address_hash)
  
  @config['ChefDataBag'].save_addresses_bag

  @options['dont_remove_address_or_server'] = true #flag to make sure our entry isnt removed in addresses bag

  @config['stateless_action'].full_bootstrap #bootstrap server with ruby and attach it to the chef server
end

#compile_audit_log(out = []) ⇒ Object



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/cheftacular/stateless_actions/compile_audit_log.rb', line 13

def compile_audit_log out=[]
  compiled_audit_hash = {}

  @config['chef_environments'].each do |env|
    @config['initializer'].initialize_data_bags_for_environment env, false, ['audit']

    @config['initializer'].initialize_audit_bag_contents env

    @config[env]['audit_bag_hash']['audit_log'].each_pair do |day, time_log_hash|
      compiled_audit_hash[day] ||= {}
      time_log_hash.each_pair do |time, log_array|
        compiled_audit_hash[day][time] ||= []
        compiled_audit_hash[day][time] <<  log_array
      end
    end
  end

  compiled_audit_hash.keys.sort.each do |day|
    out << "# Audit Log Entries for #{ Date.parse(day) }"

    entry_count, int_times = 1, []

    compiled_audit_hash[day].keys.each do |time|
      out << "#{ entry_count }. #{ time }"

      log_array_entry_count = 1

      compiled_audit_hash[day][time].each do |log_arr|
        log_arr.each do |log_hash|
          out << "    #{ log_array_entry_count }. #{ log_hash['command'] }"
          out << "        1. Hostname:  #{ log_hash['hostname'] }"
          out << "        3. Arguments: #{ log_hash['arguments'] }"
          out << "        4. Options:   #{ log_hash['options'].to_hash }"
          out << ""

          log_array_entry_count += 1
        end
      end if compiled_audit_hash[day].has_key?(time)

      entry_count += 1
    end
  end

  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")) }
end

#compile_readme(out = []) ⇒ Object



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/cheftacular/stateless_actions/compile_readme.rb', line 11

def compile_readme out=[]
   @config['action_documentation'].public_methods(false).each do |method|
     @config['action_documentation'].send(method)
  end

  @config['stateless_action_documentation'].public_methods(false).each do |method|
     @config['stateless_action_documentation'].send(method)
  end

  out << '# Table of Contents for Cheftacular Commands'

  out << '1. [Cheftacular Arguments and Flags](https://github.com/SocialCentivPublic/cheftacular/blob/master/lib/cheftacular/README.md#arguments-and-flags-for-cheftacular)'

  out << '2. [Application Commands](https://github.com/SocialCentivPublic/cheftacular/blob/master/lib/cheftacular/README.md#commands-that-can-be-run-in-the-application-context)'

  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"
  
  out << @config['documentation']['arguments']

  out << "\n## Commands that can be run in the application context"

  out << @config['helper'].compile_documentation_lines('application')

  out << "\n## Commands that can ONLY be run in the devops context"

  out << @config['helper'].compile_documentation_lines('stateless_action')

  FileUtils.rm("#{ @config['locs']['chef-log'] }/README.md") if File.exist?("#{ @config['locs']['chef-log'] }/README.md")

  File.open("#{ @config['locs']['chef-log'] }/README.md", "w") { |f| f.write(out.flatten.join("\n\n")) }
end

#coreos_bootstrap(out = []) ⇒ Object



3
4
5
# File 'lib/cheftacular/stateless_actions/bootstrappers/coreos_bootstrap.rb', line 3

def coreos_bootstrap out=[]
  raise "Not yet implemented!"
end

#create_git_key(oauth_key = "") ⇒ Object



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/cheftacular/stateless_actions/create_git_key.rb', line 19

def create_git_key oauth_key=""

  case ARGV[1]
  when nil      then raise "Too few arguments, please enter the filename of the id_rsa file you want to use"
  when 'id_rsa' then raise "Sorry, you can't use your default id_rsa"
  else               key_file = ARGV[1]
  end

  case ARGV[2]
  when nil then display_oauth_notice = true
  else          oauth_key = ARGV[2]
  end

  data = File.read("#{ @config['locs']['chef'] }/#{ key_file }")
  data_pub = File.read("#{ @config['locs']['chef'] }/#{ key_file }.pub")

  hash = @config['default']['authentication_bag_hash']

  if hash.has_key?('private_key') 
    puts "Overwrite current git key in default data bag? (Y/y/N/n)"
    input = STDIN.gets.chomp

    overwrite = (input =~ /Y|y|yes/) == 0
  else overwrite = true
  end

  if overwrite

    hash['git_private_key'] = data

    hash['git_public_key'] = data_pub

    hash['git_OAuth'] = oauth_key

    @config['ChefDataBag'].save_authentication_bag

    if oauth_key.blank?
      puts "REMEMBER! You need to put a OAuth token into this data bag item!"
      puts "You need to go to github and get the auth_token for the hiplogiq deploy user!"
      puts "Copy the key and paste it inbetween the quotes.\n"
      puts "\"Oauth\": \"<PASTE YOUR OAUTH KEY HERE>\"\n\n"
      puts "Please run \nknife data bag edit default authentication --secret-file #{ @config['locs']['chef'] }/#{ @config['cheftacular']['data_bag_key_file'] }\n"
    end
  end
end

#disk_report(disk_hash = {}, out = []) ⇒ Object



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/cheftacular/stateless_actions/disk_report.rb', line 13

def disk_report disk_hash={}, out=[]
  
  nodes = @config['getter'].get_true_node_objects(true)

  nodes = @config['parser'].exclude_nodes( nodes, [{ if: { env: '_default' }}] )

  @config['chef_environments'].each do |env|
    @config['initializer'].initialize_data_bags_for_environment env, false, ['addresses', 'server_passwords']

    @config['initializer'].initialize_passwords env
  end

  options, locs, ridley, logs_bag_hash, pass_bag_hash, bundle_command, cheftacular, passwords = @config['helper'].set_local_instance_vars

  on ( nodes.map { |n| "deploy@" + n.public_ipaddress } ), in: :groups, limit: 5, wait: 2 do |host|
    n = get_node_from_address(nodes, host.hostname)

    puts "Beginning disk report run for #{ n.name } (#{ n.public_ipaddress })"

    disk_hash[n.name] = start_disk_report( n.name, n.public_ipaddress, options, locs, passwords)
  end

  disk_hash.each_pair do |serv_name, output|
    out << "#{ serv_name }:"

    line_count = 1

    output.join("\n").split("\n").each do |line|
      out << line_count == 1 ? "               #{ line }" : "  #{ line }"

      line_count += 1
    end

    out << "\n"
  end

  puts(out) if @options['no_logs'] || @options['verbose']

  log_loc, timestamp = @config['helper'].set_log_loc_and_timestamp

  puts("Generating log file for disk report at #{ log_loc }/disk-report-#{ timestamp }.txt") unless @options['quiet']

  File.open("#{ log_loc }/disk-report-#{ timestamp }.txt", "w") { |f| f.write(out.join("\n").scrub_pretty_text) } unless @options['no_logs']
end

#environment(type = "boot", ask_on_destroy = false, remove = true) ⇒ Object



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/cheftacular/stateless_actions/environment.rb', line 23

def environment type="boot", ask_on_destroy=false, remove=true
  ask_on_destroy = case @options['env']
                   when 'staging'    then true
                   when 'production' then true
                   else                   false
                   end

  type = ARGV[1] if ARGV[1]

  raise "Unknown type: #{ type }, can only be 'boot' or 'destroy'" unless (type =~ /boot|destroy/) == 0

  nodes = @config['getter'].get_true_node_objects(true)

  nodes = @config['parser'].exclude_nodes( nodes, [{ unless: { env: @options['env'] }}])

  @options['force_yes']  = true
  @options['in_scaling'] = true

  initial_servers = @config['cheftacular']['env_boot_nodes']["#{ @options['env'] }_nodes"]

  if initial_servers.empty?
    puts "There are no servers defined for #{ @options['env'] } in the env_boot_nodes hash in your cheftacular.yml... Exiting"

    exit
  end

  case type
  when 'boot'
    initial_servers.each_pair do |name, config_hash|
      next if nodes.map { |n| n.name }.include?(name)

      @options['node_name']   = name
      @options['flavor_name'] = config_hash.has_key?('flavor') ? config_hash['flavor'] : @config['cheftacular']['default_flavor_name']
      @options['descriptor']  = config_hash.has_key?('descriptor') ? config_hash['descriptor'] : name
      @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')

      puts("Preparing to boot server #{ @options['node_name'] } for #{ @options['env'] }!") unless @options['quiet']

      @config['stateless_action'].cloud_bootstrap

      sleep 15
    end

    @config['ChefDataBag'].save_server_passwords_bag

    @options['node_name'] = nil

    @options['role'] = 'all'

    @config['action'].deploy

    #TODO INTEGRATE backups TO LOAD DATA INTO THE NEWLY BOOTED ENV
  when 'destroy'
    if ask_on_destroy
      puts "Preparing to delete nodes in #{ @options['env'] }.\nEnter Y/y to confirm."

      input = STDIN.gets.chomp

      remove = false unless ( input =~ /y|Y|yes|Yes/ ) == 0
    end

    return false unless remove

    @options['delete_server_on_remove'] = true

    nodes.each do |node|
      @options['node_name'] = node.name

      puts("Preparing to destroy server #{ @options['node_name'] } for #{ @options['env'] }!") unless @options['quiet']

      @config['stateless_action'].remove_client

      sleep 15
    end
  end
end

#fedora_bootstrap(out = []) ⇒ Object



3
4
5
# File 'lib/cheftacular/stateless_actions/bootstrappers/fedora_bootstrap.rb', line 3

def fedora_bootstrap out=[]
  raise "Not yet implemented!"
end

#fetch_file(out = []) ⇒ Object



19
20
21
22
# File 'lib/cheftacular/stateless_actions/fetch_file.rb', line 19

def fetch_file out=[]
  #TODO
  raise "Not yet implemented"
end

#fix_known_hostsObject



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/cheftacular/stateless_actions/fix_known_hosts.rb', line 19

def fix_known_hosts
  targets = ["all"]

  if ARGV[1].class == String
    targets = [ARGV[1]]
  end

  if targets.first == 'all'
    nodes = @config['getter'].get_true_node_objects(true)
    arr = []

    @config['chef_environments'].each do |env|
      @config['initializer'].initialize_data_bags_for_environment env, false, ['addresses']

      @config['initializer'].initialize_addresses_bag_contents env

      @config[env]['addresses_bag_hash']['addresses'].each do |serv_hash|
        arr << serv_hash['dn'].split('.').first
        arr << serv_hash['public']
      end
    end

    targets = arr.uniq
  end

  targets.each do |target|
    case CONFIG['host_os']
    when /mswin|windows/i
      raise "#{ __method__ } does not support this operating system at this time"
    when /linux|arch/i
      cleanup_known_hosts_for_BSD_linux_architecture target
    when /sunos|solaris/i
      raise "#{ __method__ } does not support this operating system at this time"
    when /darwin/i
      cleanup_known_hosts_for_BSD_linux_architecture target
    else
      raise "#{ __method__ } does not support this operating system at this time"
    end
  end
end

#full_bootstrapObject



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# File 'lib/cheftacular/stateless_actions/full_bootstrap.rb', line 13

def full_bootstrap
  raise "This action can only be performed if the mode is set to devops" if !@config['helper'].running_in_mode?('devops') && !@options['in_scaling']

  @options['address']     = ARGV[1] unless @options['address']
  @options['client_pass'] = ARGV[2] unless @options['client_pass']
  @options['node_name']   = ARGV[3] unless @options['node_name']

  case @config['cheftacular']['preferred_cloud_os']
  when 'ubuntu' || 'debian' then @config['stateless_action'].ubuntu_bootstrap
  else                           @config['stateless_action'].instance_eval("#{ @config['cheftacular']['preferred_cloud_os'] }_bootstrap")
  end

  @config['initializer'].initialize_passwords @options['env'] #reset the passwords var to contain the new deploy pass set in ubuntu_bootstrap

  @config['stateless_action'].chef_bootstrap
end

#get_active_ssh_connectionsObject



13
14
15
16
# File 'lib/cheftacular/stateless_actions/get_active_ssh_connections.rb', line 13

def get_active_ssh_connections
  # netstat -atn | grep ':22'
  raise "Not yet implemented"
end

#get_haproxy_logObject



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/cheftacular/stateless_actions/get_haproxy_log.rb', line 19

def get_haproxy_log
  nodes = @config['getter'].get_true_node_objects true

  nodes = @config['parser'].exclude_nodes( nodes, [{ unless: "role[#{ @config['cheftacular']['haproxy_config']['role_name'] }]" }, { if: { not_env: @options['env'] } }])

  #this must always precede on () calls so they have the instance variables they need
  options, locs, ridley, logs_bag_hash, pass_bag_hash, bundle_command, cheftacular, passwords = @config['helper'].set_local_instance_vars

  #on is namespaced to SSHKit::Backend::Netssh.on 
  on ( nodes.map { |n| "deploy@" + n.public_ipaddress } ) do |host|
    n = get_node_from_address(nodes, host.hostname)

    puts("Beginning haproxy log generation run for #{ n.name } (#{ n.public_ipaddress })") unless options['quiet']

    start_haproxy_log_generator( n.name, n.public_ipaddress, options, locs, cheftacular, passwords)
  end
end

#get_log_from_bagObject



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/cheftacular/stateless_actions/get_log_from_bag.rb', line 15

def get_log_from_bag
  #TODO https://stackoverflow.com/questions/17882463/compressing-large-string-in-ruby
  log_loc, timestamp = @config['helper'].set_log_loc_and_timestamp

  @options['role'] = 'all' unless @options['role']

  nodes = @config['getter'].get_true_node_objects(true)

  nodes = @config['parser'].exclude_nodes( nodes, [{ unless: { env: @options['env'] }}])

  nodes.each do |node|
    if @config[@options['env']]['logs_bag_hash'].has_key?("#{ node.name }-run")
      puts("Found log data in logs bag. Outputting to #{ log_loc }/stashedlog/#{ node.name }-deploystash-#{ @config[@options['env']]['logs_bag_hash']["#{ node.name }-run"][:timestamp] }.txt") unless @options['quiet']

      File.open("#{ log_loc }/stashedlog/#{ node.name }-deploystash-#{@config[@options['env']]['logs_bag_hash']["#{ node.name }-run"][:timestamp] }.txt", "w") do |f|
        f.write(@config[@options['env']]['logs_bag_hash']["#{ node.name }-run"][:text])
      end

      puts(@config[@options['env']]['logs_bag_hash']["#{ node.name }-run"][:text]) if @options['verbose']
    end
  end
end

#get_pg_pass(clip = false, target_repos = []) ⇒ Object



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/cheftacular/stateless_actions/get_pg_pass.rb', line 15

def get_pg_pass clip=false, target_repos=[]

  @config['parser'].parse_role(@options['role'])

  clip = ARGV[1] == 'clip'

  if @options['role']
    target_repos << @config['cheftacular']['repositories'][@options['role']]
  else
    @config['cheftacular']['repositories'].each_pair do |short_repo_name, repo_hash|
      target_repos << repo_hash if repo_hash['database'] == 'postgresql'
    end
  end

  target_repos.each do |repo_hash|
    db_user  = repo_hash['application_database_user']
    database = repo_hash.has_key?('custom_database_name') ? repo_hash['custom_database_name'] : repo_hash['repo_name']
    password = @config[@options['env']]['chef_passwords_bag_hash']['pg_pass']

    if @config[@options['env']]['chef_passwords_bag_hash'].has_key?(repo_hash['repo_name']) && @config[@options['env']]['chef_passwords_bag_hash'][repo_hash['repo_name']].has_key?('pg_pass')
      password = @config[@options['env']]['chef_passwords_bag_hash'][repo_hash['repo_name']]['pg_pass'] unless @config[@options['env']]['chef_passwords_bag_hash'][repo_hash['repo_name']]['pg_pass'].empty?
    end
    
    puts "postgres password for user #{ db_user } in database #{ database }_#{ @options['env'] } is #{ password }"
  end

  if clip && target_repos.count == 1
    case CONFIG['host_os']
    when /mswin|windows/i
      raise "#{ __method__ } does not support this operating system at this time"
    when /linux|arch/i
      raise "#{ __method__ } does not support this operating system at this time"
    when /sunos|solaris/i
      raise "#{ __method__ } does not support this operating system at this time"
    when /darwin/i
      `echo '#{ password }' | pbcopy`
    else
      raise "#{ __method__ } does not support this operating system at this time"
    end
  elsif clip && target_repos.count > 1
    puts "Unable to insert database string into clipboard, please copy paste as normal"
  end
end

#get_shorewall_allowed_connections(master_log_data = '') ⇒ Object



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/cheftacular/stateless_actions/get_shorewall_allowed_connections.rb', line 20

def get_shorewall_allowed_connections master_log_data=''

  if ARGV[1].nil?
    raise "Please pass a NODE_NAME with -n NODE_NAME" if @options['node_name'].nil? || @options['node_name'].empty?

    nodes = @config['getter'].get_true_node_objects true

    nodes = @config['parser'].exclude_nodes(nodes, [{ unless: { env: @options['env'] }}, { unless: { node: @options['node_name'] }}], true)

    #this must always precede on () calls so they have the instance variables they need
    options, locs, ridley, logs_bag_hash, pass_bag_hash, bundle_command, cheftacular, passwords = @config['helper'].set_local_instance_vars

    #on is namespaced to SSHKit::Backend::Netssh.on 
    on ( nodes.map { |n| "deploy@" + n.public_ipaddress } ) do |host|
      n = get_node_from_address(nodes, host.hostname)

      puts("Beginning shorewall log capture run for #{ n.name } (#{ n.public_ipaddress })") unless options['quiet']

      master_log_data = start_shorewall_log_capture( n.name, n.public_ipaddress, options, locs, cheftacular, passwords)
    end
  else
    master_log_file = ARGV[1]

    raise "File not found! Did you enter the path correctly?" unless File.exist?(master_log_file)

    master_log_data = File.read(File.expand_path(master_log_file))
  end

  puts("Parsing addresses from log data...") unless @options['quiet']

  addresses = {}

  master_log_data.scan(/^.*Shorewall:net2fw:ACCEPT.*SRC=([\d]+\.[\d]+\.[\d]+\.[\d]+) DST.*DPT=80.*$/).each do |ip_address|
    addresses[ip_address] ||= 0
    addresses[ip_address] += 1
  end

  final_addresses = {}
  check_count = 0
  addresses.each_pair do |address, count|
    next if count < 100
    
    domain = `dig +short -x #{ address[0] }`.chomp.split("\n").join('|')
    domain = domain[0..(domain.length-2)]

    domain = address[0] if domain.blank?

    final_addresses[domain] ||= {}
    final_addresses[domain]['addresses'] ||= []
    final_addresses[domain]['addresses'] <<  address[0] unless final_addresses[domain]['addresses'].include?(address[0])
    final_addresses[domain]['count']     =   count   unless final_addresses[domain].has_key?('count')
    final_addresses[domain]['count']     +=  count   if final_addresses[domain].has_key?('count')

    check_count += 1

    puts("Processed #{ check_count } addresses (#{ address[0] }):#{ domain }:#{ count }") unless @options['quiet']
  end

  final_addresses = final_addresses.sort_by {|key, value_hash| value_hash['count']}.to_h

  final_addresses = Hash[final_addresses.to_a.reverse]

  ap(final_addresses) if @options['verbose']

  log_loc, timestamp = @config['helper'].set_log_loc_and_timestamp

  CSV.open(File.expand_path("#{ @config['locs']['chef-log'] }/shorewall-parse-#{ timestamp }.csv"), "wb") do |csv| 
    final_addresses.each_pair do |dns, info_hash|
      csv << [dns, info_hash['addresses'].join('|'), info_hash['count']]
    end
  end
end

#help(inference_modes = []) ⇒ Object



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/cheftacular/stateless_actions/help.rb', line 15

def help inference_modes=[]
  target_command   = @options['command'] == 'help' ? ARGV[1] : ARGV[0]
  target_command   = @config['cheftacular']['mode'] if target_command == 'current'
  target_command ||= ''

  case target_command
  when 'action'                  then inference_modes << 'action'
  when 'application' || 'devops' then inference_modes << 'both'
  when 'stateless_action'        then inference_modes << 'stateless_action'
  when ''                        then inference_modes << 'both'
  end

  if @config['helper'].is_command? target_command
    @config['action_documentation'].send(target_command)

    puts @config['documentation']['action'].flatten.join("\n\n")

  elsif @config['helper'].is_stateless_command? target_command
    @config['stateless_action_documentation'].send(target_command)

    puts @config['documentation']['stateless_action'].flatten.join("\n\n")
  end

  @config['action_documentation'].public_methods(false).each do |method|
    @config['action_documentation'].send(method)

  end if inference_modes.include?('action') || inference_modes.include?('both')

  @config['stateless_action_documentation'].public_methods(false).each do |method|
    @config['stateless_action_documentation'].send(method)

  end if inference_modes.include?('stateless_action') || inference_modes.include?('both')

  puts @config['helper'].compile_documentation_lines('action').flatten.join("\n\n") if target_command == 'action'

  puts @config['documentation']['arguments'].flatten.join("\n\n") if target_command == 'arguments'

  puts @config['helper'].compile_documentation_lines('stateless_action').flatten.join("\n\n") if target_command == 'stateless_action'

  puts @config['helper'].compile_documentation_lines('application').flatten.join("\n\n") if target_command == 'application' || target_command.empty?

  puts @config['helper'].compile_documentation_lines('devops').flatten.join("\n\n") if target_command == 'devops'

  if inference_modes.empty? && @config['helper'].is_not_command_or_stateless_command?(target_command)
    methods = @config['action_documentation'].public_methods(false) + @config['stateless_action_documentation'].public_methods(false)

    sorted_methods = methods.uniq.sort_by { |method| @config['helper'].compare_strings(target_command, method.to_s)}

    puts "Unable to find documentation for #{ target_command }, did you mean:"
    puts "    #{ sorted_methods.at(0) }"
    puts "    #{ sorted_methods.at(1) }"
    puts "    #{ sorted_methods.at(2) }\n"
    puts "If so, please run 'cft help COMMAND' with one of the above commands or run 'hip help application' to see a list of commands"
  end
end

#initialize_data_bag_contents(env = "") ⇒ Object



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/cheftacular/stateless_actions/initialize_data_bag_contents.rb', line 12

def initialize_data_bag_contents env=""
  raise "Environment does not exist on chef server!" unless @config['chef_environments'].include?(env)

  env = ARGV[1] if env.blank?

  @config['initializer'].initialize_audit_bag_contents env

  @config['initializer'].initialize_authentication_bag_contents

  @config['initializer'].initialize_chef_passwords_bag_contents env

  @config['initializer'].initialize_config_bag_contents env
    
  #@config['initializer'].initialize_server_passwords_bag_contents env

  @config['initializer'].initialize_addresses_bag_contents env

  #@config['initializer'].initialize_logs_bag_contents env

  #@config['ChefDataBag'].initialize_node_roles_bag_contents env
end

#knife_uploadObject



13
14
15
16
17
18
19
20
21
# File 'lib/cheftacular/stateless_actions/knife_upload.rb', line 13

def knife_upload
  raise "This action can only be performed if the mode is set to devops" unless @config['helper'].running_in_mode?('devops')

  puts("Starting upload...") unless @options['quiet']

  out = `knife upload / --chef-repo-path #{ @config['locs']['chef-repo'] }`

  puts out
end

#passObject



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/cheftacular/stateless_actions/pass.rb', line 15

def pass
  @options['node_name'] = ARGV[1] unless @options['node_name']

  raise "Too few arguments, please supply a node name" if ARGV.length < 2

  nodes = @config['getter'].get_true_node_objects(true)

  nodes = @config['parser'].exclude_nodes( nodes, [{ if: { not_node: @options["node_name"] } }], true )

  raise "Node not found for #{ @options['node_name'] }" if nodes.empty?

  if nodes.first.chef_environment != @options['env']
    @config['initializer'].initialize_data_bags_for_environment nodes.first.chef_environment, false, ['server_passwords']
  end

  puts "The password for #{ nodes.first.name }(#{ nodes.first.public_ipaddress }) for env #{ nodes.first.chef_environment }" +
  " is #{ @config[nodes.first.chef_environment]['server_passwords_bag_hash']["#{ nodes.first.public_ipaddress }-deploy-pass"] }"

  case CONFIG['host_os']
  when /mswin|windows/i
    raise "#{ __method__ } does not support this operating system at this time"
  when /linux|arch/i
    raise "#{ __method__ } does not support this operating system at this time"
  when /sunos|solaris/i
    raise "#{ __method__ } does not support this operating system at this time"
  when /darwin/i
    puts "Copying #{ nodes.first.name } (#{ nodes.first.public_ipaddress }) sudo password into your clipboard"
    
    `echo '#{ @config[nodes.first.chef_environment]['server_passwords_bag_hash']["#{ nodes.first.public_ipaddress }-deploy-pass"] }' | pbcopy`
  else
    raise "#{ __method__ } does not support this operating system at this time"
  end
end

#redhat_bootstrap(out = []) ⇒ Object



3
4
5
# File 'lib/cheftacular/stateless_actions/bootstrappers/centos_bootstrap.rb', line 3

def redhat_bootstrap out=[]
  raise "Not yet implemented!"
end

#reinitialize(out = []) ⇒ Object



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/cheftacular/stateless_actions/reinitialize.rb', line 13

def reinitialize out=[]
  raise "This action can only be performed if the mode is set to devops" unless @config['helper'].running_in_mode?('devops')

  @options['address']   = ARGV[1] unless @options['address']
  @options['node_name'] = ARGV[2] unless @options['node_name']

  puts("Sending up validator file...") unless @options['quiet']

  chef_user = @config['cheftacular']['deploy_user']

  out << `scp -oStrictHostKeyChecking=no #{ @config['locs']['chef'] }/chef-validator.pem #{ chef_user }@#{ @options['address'] }:/home/#{ chef_user }`

  puts("Moving validator file to chef directory on server...") unless @options['quiet']

  out << `ssh -t -oStrictHostKeyChecking=no #{ chef_user }@#{ @options['address'] } "#{ sudo(@options['address']) } mv -f /home/#{ chef_user }/chef-validator.pem /etc/chef/validator.pem"`

  puts("Removing original client.pem file from server...") unless @options['quiet']

  out << `ssh -t -oStrictHostKeyChecking=no #{ chef_user }@#{ @options['address'] } "#{ sudo(@options['address']) } rm /etc/chef/client.pem"`

  #remove_client #just in case

  puts("Starting reinitialization...") unless @options['quiet']

  out << `#{ @config['helper'].knife_bootstrap_command }`

  puts(out.last) unless @options['quiet']

  #@options['multi-step'] = true # have the upload_nodes grab the new nodes

  #upload_nodes
end

#remove_client(delete_server = false, remove = true) ⇒ Object



14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/cheftacular/stateless_actions/remove_client.rb', line 14

def remove_client delete_server=false, remove=true
  @options['node_name']               = ARGV[1] unless @options['node_name']
  @options['delete_server_on_remove'] = ARGV[2] if !@options['delete_server_on_remove'] && !@options['dont_remove_address_or_server'] && ARGV[2]
  @options['delete_server_on_remove'] = 'destroy' if delete_server || @options['delete_server_on_remove']

  raise "This action can only be performed if the mode is set to devops" if !@config['helper'].running_in_mode?('devops') && !@options['in_scaling']

  raise "The only valid argument for the 2nd argument of this command is 'destroy', please enter this or leave it blank." if ARGV[2] && ARGV[2] != 'destroy' && !@options['dont_remove_address_or_server']

  raise "Invalid arguments! Node name is blank. Please call this script as cft remove_client <node_name>" unless @options['node_name']
  
  nodes = @config['getter'].get_true_node_objects(false, true)

  nodes.each do |n|
    begin
      client = @config['ridley'].client.find(@options['node_name'])
    rescue StandardError => e
      puts "Client #{ @options['node_name'] } not found."
      return false
    end

    if @options['env'] == 'production' && !@options['force_yes']
      puts "Preparing to delete #{ @options['node_name'] } (#{ n.public_ipaddress }).\nEnter Y/y to confirm."

      input = STDIN.gets.chomp

      remove = false unless ( input =~ /y|Y|yes|Yes/ ) == 0
    end

    if remove
      puts "Removing #{ n.name } (#{ n.public_ipaddress }) from node and client list"

      @config['ridley'].node.delete(n)
      @config['ridley'].client.delete(client)

      if @options['delete_server_on_remove'] == 'destroy'
        @config['stateless_action'].cloud "server", "destroy:#{ @options['env'] }_#{ n.name }"
      end
    end

    @config[@options['env']]['addresses_bag_hash'] = @config[@options['env']]['addresses_bag'].reload.to_hash

    @config['DNS'].compile_address_hash_for_server_from_options('set_hash_to_nil')

    @config['ChefDataBag'].save_addresses_bag
  end

  puts("Done. Please verify that the output of the next line(s) match your expectations (running client-list)") if @options['verbose']
  
  puts(`client-list`) if @options['verbose']
end

#replication_status(rep_status_hash = {}, out = []) ⇒ Object



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/cheftacular/stateless_actions/replication_status.rb', line 13

def replication_status rep_status_hash={}, out=[] 
  
  nodes = @config['getter'].get_true_node_objects(true)

  primary_nodes = @config['parser'].exclude_nodes( nodes, [{ if: { env: '_default' }}, { unless: "role[db_primary]"}] )

  slave_nodes = @config['parser'].exclude_nodes( nodes, [{ if: { env: '_default' }}, { unless: "role[db_slave]"}] )

  (primary_nodes + slave_nodes).map {|n| n.chef_environment}.uniq.each do |env|
    @config['initializer'].initialize_data_bags_for_environment env, false, ['server_passwords']

    @config['initializer'].initialize_passwords env
  end

  #this must always precede on () calls so they have the instance variables they need
  options, locs, ridley, logs_bag_hash, pass_bag_hash, bundle_command, cheftacular, passwords = @config['helper'].set_local_instance_vars

  on ( primary_nodes.map { |n| "deploy@" + n.public_ipaddress } ) do |host|
    n = get_node_from_address(nodes, host.hostname)

    puts "Beginning replication status report run for #{ n.name } (#{ n.public_ipaddress })"

    env = n.name.split('_').first

    rep_status_hash[n.name] = start_replication_report( n.name, n.public_ipaddress,  options, locs, passwords)
  end

  on ( slave_nodes.map { |n| "deploy@" + n.public_ipaddress } ) do |host|
    n = get_node_from_address(nodes, host.hostname)

    puts "Beginning slave replication status report run for #{ n.name } (#{ n.public_ipaddress })"

    env = n.name.split('_').first

    rep_status_hash[n.name] = start_slave_replication_report( n.name, n.public_ipaddress,  options, locs, passwords)
  end

  rep_status_hash.each_pair do |serv_name, output|
    out << "#{ serv_name }:"

    output.join("\n").split("\n").each do |line|
      out << "  #{ line }\n"
    end

    out << "\n"
  end

  puts(out)
end

#restart_swapObject



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/cheftacular/stateless_actions/restart_swap.rb', line 17

def restart_swap

  nodes = @config['getter'].get_true_node_objects(true)

  nodes = @config['parser'].exclude_nodes( nodes, [{ if: { env: '_default' }}] )

  #this must always precede on () calls so they have the instance variables they need
  options, locs, ridley, logs_bag_hash, pass_bag_hash, bundle_command, cheftacular, passwords = @config['helper'].set_local_instance_vars

  on ( nodes.map { |n| "deploy@" + n.public_ipaddress } ), in: :groups, limit: 5, wait: 2 do |host|
    n = get_node_from_address(nodes, host.hostname)

    puts "Beginning swap restart run for #{ n.name } (#{ n.public_ipaddress })"

    start_swap_restart( n.name, n.public_ipaddress, options, locs, cheftacular, passwords)
  end
end

#rvm(command = '') ⇒ Object



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/cheftacular/stateless_actions/rvm.rb', line 38

def rvm command=''
  raise "This action can only be performed if the mode is set to devops" unless @config['helper'].running_in_mode?('devops')

  command = case ARGV[1]
            when nil                then 'list'
            when /list|list_rubies/ then 'list rubies'
            when 'install'          then "install #{ ARGV[2] }"
            when 'run'              then ARGV[2..(ARGV.length-1)].join(' ')
            when 'all_environments' then ARGV[2..(ARGV.length-1)].join(' ')
            when 'test'             then ARGV[2..(ARGV.length-1)].join(' ')
            when 'upgrade_rvm'      then 'get stable --auto-dotfiles'
            else                         'list'
            end

  if @config['cheftacular']['rvm_gpg_key'].nil? || @config['cheftacular']['rvm_gpg_key'].blank?
    raise "GPG Key not found in cheftacular.yml! Please update your rvm_gpg_key in the file!"
  end

  nodes = ARGV[1] == 'test' ? @config['getter'].get_true_node_objects : @config['getter'].get_true_node_objects(true)

  nodes = @config['parser'].exclude_nodes( nodes, [{ unless: { env: @options['env'] }}] ) unless ARGV[1] == 'all_servers'

  @config['chef_environments'].each do |env|
    @config['initializer'].initialize_data_bags_for_environment env, false, ['addresses', 'server_passwords']

    @config['initializer'].initialize_passwords env
  end if ARGV[1] == 'all_servers'

  options, locs, ridley, logs_bag_hash, pass_bag_hash, bundle_command, cheftacular, passwords = @config['helper'].set_local_instance_vars

  on ( nodes.map { |n| "deploy@" + n.public_ipaddress } ), in: :groups, limit: 5, wait: 2 do |host|
    n = get_node_from_address(nodes, host.hostname)

    puts "Beginning run of \"rvm #{ command }\" for #{ n.name } (#{ n.public_ipaddress })"

    start_rvm( n.name, n.public_ipaddress, options, locs, passwords, command, cheftacular )
  end
end

#server_updateObject

TODO refactor to handling multiple server types



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/cheftacular/stateless_actions/server_update.rb', line 19

def server_update
  raise "This action can only be performed if the mode is set to devops" unless @config['helper'].running_in_mode?('devops')

  @options['rolling_restart'] = true if ARGV[1] && ARGV[1] == 'restart'

  if @options['rolling_restart']
    puts "Preparing to do a rolling restart for all servers in env: #{ @options['env'] } (potential data loss).\nEnter Y/y to confirm, Q/q to exit completely."

    input = STDIN.gets.chomp

    @options['rolling_restart'] = false unless ( input =~ /y|Y|yes|Yes/ ) == 0

    exit if ( input =~ /y|Y|quit|Quit/ ) == 0
  end
  
  nodes = @config['getter'].get_true_node_objects true

  nodes = @config['parser'].exclude_nodes( nodes, [{ unless: { env: @options['env'] }}] )

  #this must always precede on () calls so they have the instance variables they need
  options, locs, ridley, logs_bag_hash, pass_bag_hash, bundle_command, cheftacular, passwords = @config['helper'].set_local_instance_vars

  on ( nodes.map { |n| "deploy@" + n.public_ipaddress } ), in: :groups, limit: 5, wait: 5 do |host|
    n = get_node_from_address(nodes, host.hostname)

    puts "Beginning apt-get run for #{ n.name } (#{ n.public_ipaddress })"

    log_data, timestamp = start_apt_updater( n.name, n.public_ipaddress, options, locs, passwords)

    logs_bag_hash["#{ n.name }-upgrade"] = { text: log_data.scrub_pretty_text, timestamp: timestamp }
  end

  on ( nodes.map { |n| "deploy@" + n.public_ipaddress } ), in: :groups, limit: 5, wait: 120 do |host|
    n = get_node_from_address(nodes, host.hostname)

    puts "Beginning restart run for #{ n.name } (#{ n.public_ipaddress })"

    start_sys_restarter( n.name, n.public_ipaddress, options, locs, passwords)
  end if @options['rolling_restart']

  @config['ChefDataBag'].save_logs_bag
end

#serviceObject



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/cheftacular/stateless_actions/service.rb', line 22

def service
  raise "This action can only be performed if the mode is set to devops" unless @config['helper'].running_in_mode?('devops')

  command = case ARGV[1]
            when nil                then 'list'
            when /list/             then 'list'
            when 'restart'          then "#{ ARGV[2] } restart"
            when 'stop'             then "#{ ARGV[2] } stop"
            when 'start'            then "#{ ARGV[2] } start"
            else                         'list'
            end

  raise "You did not pass a service to #{ ARGV[1] }" if ARGV[1] =~ /restart|stop|start/ && ARGV[2].nil?

  service_location = "#{ ARGV[2] }.conf"

  nodes = @config['getter'].get_true_node_objects

  nodes = @config['parser'].exclude_nodes( nodes, [{ unless: { env: @options['env'] }}], true )

  options, locs, ridley, logs_bag_hash, pass_bag_hash, bundle_command, cheftacular, passwords = @config['helper'].set_local_instance_vars

  on ( nodes.map { |n| "deploy@" + n.public_ipaddress } ) do |host|
    n = get_node_from_address(nodes, host.hostname)

    puts "Beginning run of \"service #{ command }\" for #{ n.name } (#{ n.public_ipaddress })"

    start_service_run( n.name, n.public_ipaddress, options, locs, passwords, command, cheftacular, service_location )
  end
end

#slack(message = '', channel = '') ⇒ Object



19
20
21
22
23
24
25
26
27
# File 'lib/cheftacular/stateless_actions/slack.rb', line 19

def slack message='', channel=''
  @slack_notifier ||= Slack::Notifier.new @config['cheftacular']['slack']['webhook'], username: 'Cheftacular'

  message = ARGV[1] if message.blank?
  channel = ARGV[2] if channel.blank?

  @slack_notifier.channel = channel.nil? ? @config['cheftacular']['slack']['default_channel'] : channel
  @slack_notifier.ping message
end

#test_env(split_env = "splitstaging", type = "boot") ⇒ Object



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/cheftacular/stateless_actions/test_env.rb', line 24

def test_env split_env="splitstaging", type="boot"
  raise "This action can only be performed if the mode is set to devops" unless @config['helper'].running_in_mode?('devops')

  env_index = @options['env'] == 'staging' ? 1 : 2

  split_env = ARGV[1] unless @options['env'] == 'staging'

  type = ARGV[env_index] if ARGV[env_index]

  split_envs = @config['cheftacular']['run_list_environments'][@options['env']]

  raise "Unknown split_env: #{ split_env }, can only be #{ split_envs.values.join(', ') }" unless (split_env =~ /#{ split_envs.values.join('|') }/) == 0

  raise "Unknown type: #{ type }, can only be 'boot' or 'destroy'" unless (type =~ /boot|destroy/) == 0

  nodes = @config['getter'].get_true_node_objects(true)

  nodes = @config['parser'].exclude_nodes( nodes, [{ unless: "role[#{ split_env.split('split').join('split_') }]" }, { unless: { env: @options['env'] }}])

  @options['force_yes']  = true
  @options['in_scaling'] = true

  case type
  when 'boot'
    @config['cheftacular']['split_env_nodes'].each_pair do |name, config_hash|
      config_hash           ||= {}
      true_name               = name.gsub('SPLITENV', split_env)
      @options['sub_env']     = split_env
      @options['node_name']   = "#{ true_name }#{ 'p' if @options['env'] == 'production' }" 
      @options['flavor_name'] = config_hash.has_key?('flavor') ? config_hash['flavor'] : @config['cheftacular']['default_flavor_name']
      @options['descriptor']  = config_hash.has_key?('descriptor') ? "#{ config_hash['descriptor'] }-#{ split_env }" : name
      @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')
                                
      next if nodes.map { |n| n.name }.include?(@options['node_name'])

      puts("Preparing to boot server #{ @options['node_name'] } for #{ @options['env'] }'s #{ split_env } environment!") unless @options['quiet']

      @config['stateless_action'].cloud_bootstrap

      sleep 15
    end

    @config['ChefDataBag'].save_server_passwords_bag
  when 'destroy'
    @options['delete_server_on_remove'] = true

    nodes.each do |node|

      @options['node_name'] = node.name

      puts("Preparing to destroy server #{ @options['node_name'] } for #{ @options['env'] }'s #{ split_env } environment!") unless @options['quiet']

      @config['stateless_action'].remove_client

      sleep 15
    end
  end
end

#ubuntu_bootstrap(out = []) ⇒ Object



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/cheftacular/stateless_actions/bootstrappers/ubuntu_bootstrap.rb', line 13

def ubuntu_bootstrap out=[]
  raise "This action can only be performed if the mode is set to devops" if !@config['helper'].running_in_mode?('devops') && !@options['in_scaling']

  @options['address']     = ARGV[1] unless @options['address']
  @options['client_pass'] = ARGV[2] unless @options['address']

  if `which sshpass`.empty?
    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)"
  end

  new_deploy_pass = @config['helper'].gen_pass(@config['cheftacular']['server_pass_length'])

  deploy_user = @config['cheftacular']['deploy_user']

  root_commands = [
    "cd /home",
    "adduser #{ deploy_user } --gecos \",,,,\" --disabled-password",
    "echo #{ deploy_user }:#{ new_deploy_pass } | chpasswd",
    "adduser #{ deploy_user } www-data",
    "adduser #{ deploy_user } sudo",
    "mkdir -p /home/#{ deploy_user }/.ssh",
    "touch /home/#{ deploy_user }/.ssh/authorized_keys && touch /home/#{ deploy_user }/.ssh/known_hosts",
    "chown -R #{ deploy_user }:www-data /home/#{ deploy_user }/.ssh",
    'sed -i "s/StrictModes yes/StrictModes yes\nPasswordAuthentication no\nUseDNS no\nAllowUsers deploy postgres\n/" /etc/ssh/sshd_config'.gsub('deploy', deploy_user),
    'sed -i "s/PermitRootLogin yes/PermitRootLogin no/" /etc/ssh/sshd_config'
  ]

  @config['default']['authentication_bag_hash']['authorized_keys'].each do |line|
    root_commands << "echo \"#{ line }\" >> /home/#{ deploy_user }/.ssh/authorized_keys"
  end

  sudo = "echo #{ new_deploy_pass } | sudo -S"

  deploy_commands = [
    "#{ sudo } apt-get update",
    "#{ sudo } apt-get install curl git-core shorewall -y",
    "#{ sudo } apt-get upgrade -y"
  ]

  final_commands = []

  if @config['cheftacular']['install_rvm_on_boot']
    deploy_commands << "gpg --keyserver hkp://keys.gnupg.net --recv-keys #{ @config['cheftacular']['rvm_gpg_key'] }"
    deploy_commands << "curl -L https://get.rvm.io | bash -s stable"

    final_commands

    rvm_source = "source /home/deploy/.rvm/bin/rvm &&"

    final_commands = [
      "#{ rvm_source } echo '#{ new_deploy_pass }' | rvmsudo -S rvm requirements",
      "#{ rvm_source } rvm install #{ @config['cheftacular']['ruby_version'] }",
      "#{ rvm_source } rvm install 1.9.3-p327" #chef's default ruby, we'll need it in a place rvm can find it until the symlink is made 
    ]
  end

  out << `sshpass -p "#{ @options['client_pass'] }" ssh -t -oStrictHostKeyChecking=no root@#{ @options['address'] } '#{ root_commands.join(' && ') } && service ssh restart'`

  puts("Finished initial setup...stage 1 of 3 for server #{ @options['address'] }") if @options['in_scaling']

  puts(out.last) unless @options['quiet'] || @options['in_scaling']

  deploy_commands.each do |cmnd|
    puts("(#{ @options['address'] }) Running #{ cmnd.gsub("#{ new_deploy_pass }", "sudo password") }") unless @options['quiet'] || @options['in_scaling']
    out << `ssh -t -oStrictHostKeyChecking=no #{ deploy_user }@#{ @options['address'] } "#{ cmnd }"`

    puts(out.last) unless @options['quiet'] || @options['in_scaling']
  end

  puts("Finished deploy setup....stage 2 of 3 for server #{ @options['address'] }") if @options['in_scaling']

  final_commands.each do |cmnd|
    puts "(#{ @options['address'] }) Running #{ cmnd.gsub("#{ new_deploy_pass }", "sudo password") }"
    out << `ssh -t -oStrictHostKeyChecking=no #{ deploy_user }@#{ @options['address'] } "#{ cmnd }"`

    puts(out.last) unless @options['quiet'] || @options['in_scaling']
  end

  puts("Finished ruby setup......stage 3 of 3 for server #{ @options['address'] }") if @options['in_scaling']

  @config[@options['env']]['server_passwords_bag_hash']["#{ @options['address'] }-root-pass"] = @options['client_pass']

  @config[@options['env']]['server_passwords_bag_hash']["#{ @options['address'] }-deploy-pass"] = new_deploy_pass

  @config[@options['env']]['server_passwords_bag_hash']["#{ @options['address'] }-name"] = @options['node_name'] if @options['node_name']

  @config['ChefDataBag'].save_server_passwords_bag unless @options['in_scaling']
end

#update_cloudflare_dns_from_cloudObject



23
24
25
26
27
28
29
30
31
32
33
# File 'lib/cheftacular/stateless_actions/update_cloudflare_dns_from_cloud.rb', line 23

def update_cloudflare_dns_from_cloud
  raise "This action can only be performed if the mode is set to devops" unless @config['helper'].running_in_mode?('devops')

  target_domain = @config[@options['env']]['config_bag_hash'][@options['sub_env']]['tld']

  @config['stateless_action'].update_tld 'self' unless ARGV[1] == 'skip_update_tld'

  target_domain_records = @config['stateless_action'].cloud('domain', "read:#{ target_domain }")["records_for_#{ target_domain }"]

  @config['DNS'].update_cloudflare_from_array_of_domain_hashes target_domain, target_domain_records
end

#update_split_branchesObject



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/cheftacular/stateless_actions/update_split_branches.rb', line 25

def update_split_branches
  target_loc = @config['helper'].running_in_mode?('application') ? @config['locs']['app-root'] : "#{ @config['locs']['root'] }/#{ @options['repository'] }"

  current_revision = `cd #{ target_loc } && git rev-parse --abbrev-ref HEAD`
  
  puts "Preparing to run merges..."

  split_branch_repos =  @config['getter'].get_split_branch_hash

  raise "unsupported codebase, please run in #{ split_branch_repos.keys.join(', ') } only!" if ( @options['repository'] =~ /#{ split_branch_repos.keys.join('|') }/ ) == 0

  test_for_changes = `cd #{ target_loc } && git diff --exit-code`

  unless test_for_changes.empty?
    puts "You have changes in your current working tree for #{ target_loc }. Please commit these changes before running this command."

    exit
  end

  commands = [
    "cd #{ target_loc }",
    "git checkout master",
    "git pull origin master",
    "git fetch origin",
  ]

  @config['run_list_environments'].each_pair do |env, branch_hash|
    branch_hash.keys.each do |branch_name|        
      true_branch_name = branch_name.gsub('_','-')

      commands << ["git checkout #{ true_branch_name }", "git pull origin #{ true_branch_name }", 'git merge master --no-edit', "git push origin #{ true_branch_name }"]
    end
  end

  commands << "git checkout #{ current_revision }"

  puts `#{ commands.flatten.join(' && ') }` unless @options['quiet']

  puts "Update split branches complete. You have been returned to the branch you were on before which was \"#{ current_revision.chomp }\"."
end

#update_tld(target_tld = "") ⇒ Object



14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/cheftacular/stateless_actions/update_tld.rb', line 14

def update_tld target_tld=""
  raise "This action can only be performed if the mode is set to devops" unless @config['helper'].running_in_mode?('devops')
  
  raise "Undefined new tld to migrate to" if ARGV.length <= 1 && target_tld.blank? 

  nodes = @config['getter'].get_true_node_objects(true)

  #We need to manually update beta nodes as they share the same env space as their non-beta counterparts TODO Refactor?
  nodes = @config['parser'].exclude_nodes( nodes, [{ unless: { env: @options['env'] }}] )

  address_hash = @config['getter'].get_addresses_hash @options['env']

  target_tld = ARGV[1] if target_tld.blank?

  old_tld = @config[@options['env']]['config_bag_hash'][@options['sub_env']]['tld']

  if target_tld == 'self'
    target_tld = old_tld
  end

  nodes.each do |n|

    @options['node_name'] = n.name

    domain_obj = PublicSuffix.parse address_hash[n.public_ipaddress]['dn']

    next unless domain_obj.domain == old_tld #we can't create records for domains not managed under the environment's tld

    #TODO CHECK CLOUD IF TLD EXISTS

    specific_domain = "#{ domain_obj.trd }.#{ target_tld }"

    if specific_domain != "#{ @options['node_name'] }.#{ target_tld }"
      @config['DNS'].create_dns_record_for_domain_from_address_hash(specific_domain, address_hash[n.public_ipaddress], "specific_domain_mode")
    end

    @config['DNS'].create_dns_record_for_domain_from_address_hash(target_tld, address_hash[n.public_ipaddress])

    @config['DNS'].compile_address_hash_for_server_from_options("set_specific_domain_name:#{ specific_domain }")

    sleep 1 #prepare for next domain
  end

  @config[@options['env']]['config_bag_hash'][@options['sub_env']]['tld'] = target_tld

  puts "BAG TLD::#{ @config[@options['env']]['config_bag_hash'][@options['sub_env']]['tld'] }"

  @config['ChefDataBag'].save_config_bag
  @config['ChefDataBag'].save_addresses_bag
end

#upload_nodes(invalidate_file_node_cache = false) ⇒ Object



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/cheftacular/stateless_actions/upload_nodes.rb', line 27

def upload_nodes invalidate_file_node_cache=false
  raise "This action can only be performed if the mode is set to devops" if !@config['helper'].running_in_mode?('devops') && !@options['in_scaling']

  @config['chef_environments'].each do |env|
    @config['initializer'].initialize_data_bags_for_environment env, false, ['node_roles']

    @config['initializer'].initialize_node_roles_bag_contents env
  end

  nodes = @options['multi-step'] ? @config['getter'].get_true_node_objects(true,true) : @config['getter'].get_true_node_objects(true)

  node_roles_hash, bag_hash, allowed_changes_hash = {},{},{}

  Dir.foreach(@config['locs']['nodes']) do |fr|
    next if @config['helper'].is_junk_filename?(fr)

    Dir.foreach("#{ @config['locs']['nodes'] }/#{ fr }") do |f|
      next if @config['helper'].is_junk_filename?(f)

      node_roles_hash[f.split('.json').first] = JSON.parse(File.read("#{ @config['locs']['nodes'] }/#{ fr }/#{ f }"))
    end
  end if @config['helper'].running_in_mode?('devops') #only devops modes should have a nodes_dir

  @config['chef_environments'].each do |env|
    @config[env]['node_roles_bag_hash']['node_roles'].each_pair do |role_name, role_hash|
      bag_hash[role_hash['name']] = role_hash.to_hash #hashes from chef server are stored as hashie objects until forced into hashes
    end
  end

  if !@options['force_yes'] && @config['helper'].running_in_mode?('devops') 
    node_roles_hash.each_pair do |role_name, role_hash|
      overwrite = false
      if bag_hash[role_name] != role_hash
        puts "Detected difference between saved roles hash and updated node_roles json hash for #{ role_name }."

        puts "Saved roles hash:"
        ap(bag_hash[role_name])

        puts "New roles hash:"
        ap(role_hash)

        puts "Preparing to overwrite the saved roles hash with the node_roles hash, enter Y/y to confirm."

        input = STDIN.gets.chomp

        overwrite = true if ( input =~ /y|Y|yes|Yes/ ) == 0

        allowed_changes_hash[role_name] = role_hash if overwrite
      else #bag_hash does not have a key for that role, populate it.
        allowed_changes_hash[role_name] = role_hash
      end

      @config[role_hash['chef_environment']]['node_roles_bag_hash']['node_roles'][role_name] = role_hash
    end
  else
    allowed_changes_hash = bag_hash
  end

  #force add any roles that are not in the bag in the event force yes is turned on
  (node_roles_hash.keys - bag_hash.keys).each do |role_not_in_node_roles_bag|

    new_role = node_roles_hash[role_not_in_node_roles_bag]

    allowed_changes_hash[role_not_in_node_roles_bag] = bag_hash[role_not_in_node_roles_bag]

    @config[new_role['chef_environment']]['node_roles_bag_hash']['node_roles'][new_role['name']] = new_role
  end if @options['force_yes'] && @config['helper'].running_in_mode?('devops')

  nodes.each do |node|
    # if there is a node_roles file that completely matches the name of the file, use it
    changes_for_current_node = false

    if allowed_changes_hash[node.name]
      allowed_changes_hash[node.name].each_pair do |node_key, node_val|
        if (node_key =~ /name/) != 0 && node.send(node_key) != node_val
          puts("Updating #{ node.name } with attribute #{ node_key } = #{ node_val } from #{ node.name }.json") unless @options['quiet']

          node.send("#{ node_key }=", node_val)

          changes_for_current_node, invalidate_file_node_cache = true, true
        end
      end

    elsif allowed_changes_hash.keys.include?(node.name.gsub(/\d/,'')) #if there is a template file that matches the stripped down name, use it
      allowed_changes_hash[node.name.gsub(/\d/,'')].each_pair do |node_key, node_val|
        if (node_key =~ /name/) != 0 && node.send(node_key) != node_val
          puts("Updating #{ node.name } with attribute #{ node_key } = #{ node_val } from template json file") unless @options['quiet']
          
          node.send("#{ node_key }=", node_val)

          changes_for_current_node, invalidate_file_node_cache = true, true
        end
      end
    end

    node.save if changes_for_current_node
  end

  @config['chef_environments'].each do |env|
    @config['ChefDataBag'].save_node_roles_bag env
  end if !@options['force_yes'] && @config['helper'].running_in_mode?('devops')

  @config['helper'].cleanup_file_caches('current') if invalidate_file_node_cache
end

#upload_rolesObject



12
13
14
15
16
17
18
19
20
21
22
# File 'lib/cheftacular/stateless_actions/upload_roles.rb', line 12

def upload_roles
  raise "This action can only be performed if the mode is set to devops" if !@config['helper'].running_in_mode?('devops') && !@options['in_scaling']

  Dir.foreach(@config['locs']['roles']) do |rd|
    next if @config['helper'].is_junk_filename?(rd)

    puts("Loading in role from file #{ rd }") if @options['verbose']

    puts `knife role from file "#{ @config['locs']['roles'] }/#{ rd }"` 
  end
end

#vyatta_bootstrap(out = []) ⇒ Object



3
4
5
# File 'lib/cheftacular/stateless_actions/bootstrappers/vyatta_bootstrap.rb', line 3

def vyatta_bootstrap out=[]
  raise "Not yet implemented!"
end