Class: Cheftacular::StatelessAction

Inherits:
Object
  • Object
show all
Includes:
RbConfig, SSHKit::DSL
Defined in:
lib/cheftacular/stateless_action.rb,
lib/cheftacular/stateless_actions/rvm.rb,
lib/cheftacular/stateless_actions/ssh.rb,
lib/cheftacular/stateless_actions/file.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/service.rb,
lib/cheftacular/stateless_actions/version.rb,
lib/cheftacular/stateless_actions/test_env.rb,
lib/cheftacular/stateless_actions/arguments.rb,
lib/cheftacular/stateless_actions/reset_bag.rb,
lib/cheftacular/stateless_actions/update_tld.rb,
lib/cheftacular/stateless_actions/chef_server.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/role_toggle.rb,
lib/cheftacular/stateless_actions/cleanup_logs.rb,
lib/cheftacular/stateless_actions/clear_caches.rb,
lib/cheftacular/stateless_actions/env_ssh_exec.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/compile_readme.rb,
lib/cheftacular/stateless_actions/create_git_key.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/update_cookbook.rb,
lib/cheftacular/stateless_actions/get_log_from_bag.rb,
lib/cheftacular/stateless_actions/location_aliases.rb,
lib/cheftacular/stateless_actions/compile_audit_log.rb,
lib/cheftacular/stateless_actions/add_ssh_key_to_bag.rb,
lib/cheftacular/stateless_actions/cheftacular_config.rb,
lib/cheftacular/stateless_actions/replication_status.rb,
lib/cheftacular/stateless_actions/update_chef_client.rb,
lib/cheftacular/stateless_actions/update_cheftacular.rb,
lib/cheftacular/stateless_actions/cheftacular_yml_help.rb,
lib/cheftacular/stateless_actions/list_toggleable_roles.rb,
lib/cheftacular/stateless_actions/update_split_branches.rb,
lib/cheftacular/stateless_actions/clean_server_passwords.rb,
lib/cheftacular/stateless_actions/chef_bootstrap_from_queue.rb,
lib/cheftacular/stateless_actions/full_bootstrap_from_queue.rb,
lib/cheftacular/stateless_actions/check_cheftacular_yml_keys.rb,
lib/cheftacular/stateless_actions/cloud_bootstrap_from_queue.rb,
lib/cheftacular/stateless_actions/get_active_ssh_connections.rb,
lib/cheftacular/stateless_actions/initialize_cheftacular_yml.rb,
lib/cheftacular/stateless_actions/initialize_data_bag_contents.rb,
lib/cheftacular/stateless_actions/update_cloudflare_dns_from_cloud.rb,
lib/cheftacular/stateless_actions/bootstrappers/centos_bootstrap_from_queue.rb,
lib/cheftacular/stateless_actions/bootstrappers/coreos_bootstrap_from_queue.rb,
lib/cheftacular/stateless_actions/bootstrappers/fedora_bootstrap_from_queue.rb,
lib/cheftacular/stateless_actions/bootstrappers/redhat_bootstrap_from_queue.rb,
lib/cheftacular/stateless_actions/bootstrappers/ubuntu_bootstrap_from_queue.rb,
lib/cheftacular/stateless_actions/bootstrappers/vyatta_bootstrap_from_queue.rb,
lib/cheftacular/stateless_actions/update_the_cheftacular_cookbook_and_knife_upload.rb

Instance Method Summary collapse

Constructor Details

#initialize(options, config) ⇒ StatelessAction

Returns a new instance of StatelessAction.



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

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?



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

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.gsub("'",'')
  else
    @config['default']['authentication_bag_hash']["specific_authorized_keys"] << public_ssh_key.gsub("'",'')
  end

  @config['ChefDataBag'].save_authentication_bag
end

#argumentsObject Also known as: flags



84
85
86
87
88
# File 'lib/cheftacular/stateless_actions/arguments.rb', line 84

def arguments
  @config['stateless_action_documentation'].arguments

  puts @config['documentation']['arguments']
end

#check_cheftacular_yml_keys(out = [], exit_on_missing = false, warn_on_missing = 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
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/cheftacular/stateless_actions/check_cheftacular_yml_keys.rb', line 16

def check_cheftacular_yml_keys out=[], exit_on_missing=false, warn_on_missing=false
  base_message = "Your cheftacular.yml is missing the key KEY, its default value is being set to DEFAULT for this run."
  
  #############################2.13.0################################################

  unless @config['cheftacular'].has_key?('pleasantries')
    @config['cheftacular']['pleasantries'] = true
  end

  #############################2.11.0################################################

  unless @config['cheftacular'].has_key?('server_creation_tries')
    @config['cheftacular']['server_creation_tries'] = 2
  end

  unless @config['cheftacular']['backup_config'].has_key?('db_primary_role')
    puts base_message.gsub('KEY', 'backup_config:db_primary_role').gsub('DEFAULT', 'db_primary')

    @config['cheftacular']['backup_config']['db_primary_role'] = 'db_primary'

    warn_on_missing = true
  end

  unless @config['cheftacular'].has_key?('git')
    puts(base_message.gsub('KEY', 'git').split(',').first + ', Please add the git high level key to your cheftacular.yml.')

    @config['cheftacular']['git'] ||= {}

    warn_on_missing = true
  end

  unless @config['cheftacular']['git'].has_key?('check_remote_for_branch_existence')
    puts base_message.gsub('KEY', 'git:check_remote_for_branch_existence').gsub('DEFAULT', 'false')

    @config['cheftacular']['git']['check_remote_for_branch_existence'] = false

    warn_on_missing = true
  end

  #############################2.10.0################################################

  unless @config['cheftacular'].has_key?('self_update_repository')
    puts base_message.gsub('KEY', 'self_update_repository').gsub('DEFAULT', 'blank')

    @config['cheftacular']['self_update_repository'] = ''

    warn_on_missing = true
  end

  #############################2.9.2################################################

  unless @config['cheftacular']['slack'].has_key?('notify_on_command_execute')
    puts base_message.gsub('KEY', 'slack:notify_on_command_execute').gsub('DEFAULT', 'blank')

    @config['cheftacular']['slack']['notify_command_execute'] = ''

    warn_on_missing = true
  end

  #############################2.9.0################################################

  unless @config['cheftacular']['slack'].has_key?('notify_on_deployment_args')
    puts base_message.gsub('KEY', 'slack:notify_on_deployment_args').gsub('DEFAULT', 'blank')

    @config['cheftacular']['slack']['notify_on_deployment_args'] = ''

    warn_on_missing = true
  end

  #############################2.7.0################################################

  unless @config['cheftacular'].has_key?('backup_config')
    puts base_message.gsub('KEY', 'backup_config').gsub('DEFAULT', 'nil')

    warn_on_missing = true
  end

  #############################2.6.0################################################
  unless @config['cheftacular'].has_key?('route_dns_changes_via')
    puts base_message.gsub('KEY', 'route_dns_changes_via').gsub('DEFAULT', @options['preferred_cloud'])

    @config['cheftacular']['route_dns_changes_via'] = @options['preferred_cloud']

    warn_on_missing = true
  end

  unless @config['cheftacular'].has_key?('node_name_separator')
    puts base_message.gsub('KEY', 'node_name_separator').gsub('DEFAULT', '-')

    @config['cheftacular']['node_name_separator'] = '-'

    warn_on_missing = true
  end

  unless @config['cheftacular'].has_key?('cloud_authentication')
    puts (base_message.gsub('KEY', 'cloud_authentication').split(',').first + ', this is a critical issue and must be fixed.')

    exit_on_missing = true
  end

  if !@config['cheftacular'].has_key?('chef_server') && @options['command'] == 'chef_server'
    puts (base_message.gsub('KEY', 'chef_server').split(',').first + ', this is a critical issue and must be fixed to run the chef_server command.')

    exit_on_missing = true
  end

  unless @config['cheftacular'].has_key?('chef_version')
    puts (base_message.gsub('KEY', 'chef_version').split(',').first + ', this is a critical issue and must be fixed.')

    exit_on_missing = true
  end

  unless @config['cheftacular'].has_key?('pre_install_packages')
    puts base_message.gsub('KEY', 'pre_install_packages').gsub('DEFAULT', '')

    @config['cheftacular']['pre_install_packages'] = ''

    warn_on_missing = true
  end

  unless @config['cheftacular'].has_key?('role_toggling')
    puts base_message.gsub('KEY', 'role_toggling').gsub('DEFAULT', "it's default nested keys")

    @config['cheftacular']['role_toggling'] = {}
    @config['cheftacular']['role_toggling']['deactivated_role_suffix'] = '_deactivated'
    @config['cheftacular']['role_toggling']['strict_roles']            = true
    @config['cheftacular']['role_toggling']['skip_confirm']            = false
    @config['cheftacular']['role_toggling']['do_not_allow_toggling']   = true

    warn_on_missing = true
  end

  if warn_on_missing || exit_on_missing
    puts "Please enter your missing keys into your cheftacular.yml based off of the cheftacular.yml at"
    puts "\n  https://github.com/SocialCentivPublic/cheftacular/blob/master/examples/cheftacular.yml"
  end

  exit if exit_on_missing || @options['command'] == __method__
end

#chef_bootstrap_from_queue(threads = []) ⇒ Object



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

def chef_bootstrap_from_queue threads=[]
  raise "This action is not meant to be called directly!" if !@options['in_scaling'] && !@options['in_single_server_creation']

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

  execution_hash_array = compile_chef_bootstrap_commands

  @config['bootstrap_timestamp'] ||= Time.now.strftime("%Y%m%d%H%M%S")

  @config['server_creation_queue'].each do |server_hash|
    puts("#{ server_name_output(server_hash) } Starting chef-client installation...") unless @options['quiet']

    threads << Thread.new { execute_execution_hash_array(server_hash, execution_hash_array) }
  end

  threads.each { |thread| thread.join }

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

  @config['stateless_action'].upload_nodes(true)
end

#chef_serverObject



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
# File 'lib/cheftacular/stateless_actions/chef_server.rb', line 37

def chef_server
  raise "This action can only be performed if the mode is set to devops" unless @config['helper'].running_in_mode?('devops')
  
  if @config['cheftacular']['chef_server'].nil? || !@config['cheftacular']['chef_server']['interactable']
    raise "Chef server is not currently set to be interactable with the chef-server command"
  end

  command = ARGV[1]

  raise "Unsupported command (#{ command }) for cft chef_server" unless command =~ /restart|processes|memory/

  command_prefix = case @config['cheftacular']['chef_server']['ssh_user']
                   when 'root' then ''
                   else             "echo #{ @config['cheftacular']['chef_server']['sudo_pass'] } | sudo -S "
                   end

  command = case command
            when 'restart'   then command_prefix + 'chef-server-ctl restart'
            when 'processes' then 'ps aux'
            when 'memory'    then 'free -m'
            end

  options = @options

  on ( @config['cheftacular']['chef_server']['ssh_user'] + "@" + @config['parser'].parse_base_chef_server_url ) do |host|

    puts "Beginning chef-server #{ command } run"

    start_chef_server_interactor(command, options)
  end
end

#cheftacular_config(command = '') ⇒ Object Also known as: cc



29
30
31
32
33
34
35
# File 'lib/cheftacular/stateless_actions/cheftacular_config.rb', line 29

def cheftacular_config command=''
  command = ARGV[1] if command.blank?

  raise "Unsupported command (#{ command }) for cft cheftacular_config" unless command =~ /diff|display|sync|overwrite/

  self.send("cheftacular_config_#{ command }")
end

#cheftacular_yml_help(command = '', key_nesting_array = []) ⇒ Object Also known as: yml_help



34
35
36
37
38
39
40
41
42
43
44
# File 'lib/cheftacular/stateless_actions/cheftacular_yml_help.rb', line 34

def cheftacular_yml_help command='', key_nesting_array=[]
  key_to_check = ARGV[1]

  raise "This command requires a key to check the documentation, please enter one as the first argument" if key_to_check.nil?

  key_to_check.split(':').each { |key| key_nesting_array << key }

  doc_hash = YAML::load(ERB.new(IO.read(File.open(File.expand_path("#{ @config['locs']['doc'] }/cheftacular_yml_help.yml")))).result)

  traverse_documentation_hash(doc_hash, key_nesting_array)
end

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



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/clean_cookbooks.rb', line 22

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 = @config['filesystem'].parse_berkshelf_cookbook_versions

    chef_repo_cookbooks = @config['filesystem'].parse_chef_repo_cookbook_versions

    berkshelf_cookbooks.each_pair do |berkshelf_cookbook, cookbook_hash|
      if chef_repo_cookbooks.has_key?(berkshelf_cookbook) || chef_repo_cookbooks.has_key?(cookbook_hash['location']) #don't overwrite cookbooks without user input
        if local_options['interactive']
          puts "COOKBOOK::~~~~#{ berkshelf_cookbook }~~~~::VERSION::~~~~~~~~#{ cookbook_hash['version'] } VS #{ chef_repo_cookbooks[berkshelf_cookbook] }"
          puts "\nEnter O | o | overwrite to overwrite ~~~~#{ berkshelf_cookbook }~~~~ 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[berkshelf_cookbook], cookbook_hash['version'])
      end

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

#clean_server_passwordsObject



10
11
12
13
14
# 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
  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')
end

#cleanup_logs(directories_to_not_delete = '') ⇒ Object



29
30
31
32
33
34
35
36
# File 'lib/cheftacular/stateless_actions/cleanup_logs.rb', line 29

def cleanup_logs directories_to_not_delete=''
  directories_to_not_delete   = ARGV[1] if directories_to_not_delete.blank?
  directories_to_not_delete ||= ''
  
  @config['filesystem'].remove_log_directories(directories_to_not_delete.split(','))

  @config['filesystem'].initialize_log_directories(false)
end

#clear_cachesObject



27
28
29
# File 'lib/cheftacular/stateless_actions/clear_caches.rb', line 27

def clear_caches
  @config['filesystem'].cleanup_file_caches('all')
end

#client_listObject Also known as: clients, cl



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/client_list.rb', line 34

def client_list
  @config['filesystem'].cleanup_file_caches('current-nodes')

  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'
    next if @options['env'] != @config['cheftacular']['default_environment'] && env != @options['env']
    next if @options['search_env_name'] && env != @options['search_env_name']

    env_nodes = @config['parser'].exclude_nodes(nodes, [{ if: { not_env: env } }])
    puts "\nFound #{ env_nodes.count } #{ env } total nodes: (If you input search arguments the number returned will be smaller)"
    out = "  #{ 'name'.ljust(22) } #{ 'ip_address'.ljust(21) }"
    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|
      next if @options['search_node_name'] && !node.name.include?(@options['search_node_name'])
      next if @options['search_role_name'] && !node.run_list.join(', ').gsub('role[','').gsub(']','').include?(@options['search_role_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(', ').gsub('role[','').gsub(']','') }"
      
      puts out
    end
  end
end

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



124
125
126
127
128
129
130
131
132
# File 'lib/cheftacular/stateless_actions/cloud.rb', line 124

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['cheftacular'], @options)

  @config['cloud_interactor'].run args
end

#cloud_bootstrap(server_hash = {}, options_to_sync = ['node_name', 'flavor_name', 'descriptor', 'dns_config', 'address', 'private_address', 'client_pass']) ⇒ Object Also known as: cb



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
# File 'lib/cheftacular/stateless_actions/cloud_bootstrap.rb', line 33

def cloud_bootstrap server_hash={}, options_to_sync=['node_name', 'flavor_name', 'descriptor', 'dns_config', 'address', 'private_address', 'client_pass']
  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']   = server_hash['node_name']   if server_hash.has_key?('node_name')
  @options['flavor_name'] = server_hash['flavor_name'] if server_hash.has_key?('flavor_name')
  @options['descriptor']  = server_hash['descriptor']  if server_hash.has_key?('descriptor')
  @options['with_dn']     = server_hash['dns_config']  if server_hash.has_key?('dns_config')

  @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']

  @options['in_single_server_creation'] = true

  puts "Preparing to boot #{ @options['node_name'] }(#{ @options['flavor_name'] })..."

  real_node_name = @config['getter'].get_current_real_node_name

  #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:#{ real_node_name }:#{ @options['flavor_name'] }"

  status_hash = @config['stateless_action'].cloud "server", "poll:#{ real_node_name }"

  status_hash['created_servers'].each do |cloud_server_hash|
    next unless cloud_server_hash['name'] == "#{ real_node_name }"

    @options['address'], @options['private_address'] = @config['cloud_provider'].parse_addresses_from_server_create_hash cloud_server_hash
  end

  @options['client_pass'] = @config['cloud_provider'].parse_server_root_password_from_server_create_hash status_hash, real_node_name
  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

  server_hash = @config['queue_master'].sync_server_hash_into_queue(server_hash.merge(@config['helper'].return_options_as_hash(options_to_sync)))

  puts("Created server #{ server_hash['node_name'] } and attached additional flags:")                                                  unless @options['quiet']
  ap(@config['queue_master'].return_hash_from_queue('server_creation_queue', server_hash, 'node_name').except(*options_to_sync[0..3])) unless @options['quiet']

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

#cloud_bootstrap_from_queueObject



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

def cloud_bootstrap_from_queue
  raise "This action is not meant to be called directly!" unless @options['in_scaling']

  @config['in_server_creation_queue'] = true

  @config['server_creation_queue'].each do |server_hash|
    puts("Preparing to boot server #{ server_hash['node_name'] } for #{ @options['env'] }!") unless @options['quiet']

    puts("Creating server #{ server_hash['node_name'] } with arguments:") unless @options['quiet']
    ap(server_hash.except('node_name'))                                   unless @options['quiet']

    @config['stateless_action'].cloud_bootstrap(server_hash)
  end

  @config['stateless_action'].full_bootstrap_from_queue
end

#compile_audit_log(out = []) ⇒ 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
# File 'lib/cheftacular/stateless_actions/compile_audit_log.rb', line 16

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.sort.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 << @config['auditor'].compile_audit_hash_entry_as_array(log_hash, log_array_entry_count)

          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.flatten.join("\n")) }
end

#compile_readme(out = []) ⇒ 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
# File 'lib/cheftacular/stateless_actions/compile_readme.rb', line 20

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_from_queue(out = []) ⇒ Object



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

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

#create_git_key(oauth_key = "") ⇒ 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
# File 'lib/cheftacular/stateless_actions/create_git_key.rb', line 24

def create_git_key oauth_key=""
  raise "This action can only be performed if the mode is set to devops" unless @config['helper'].running_in_mode?('devops')
  
  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



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

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| @config['cheftacular']['deploy_user'] + "@" + 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

  @config['filesystem'].generate_report_from_node_hash('disk report', disk_hash)
end

#env_ssh_exec(command = '', nodes = []) ⇒ Object



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/cheftacular/stateless_actions/env_ssh_exec.rb', line 23

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

  command = ARGV[1] if command.blank? 

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

  #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| @config['cheftacular']['deploy_user'] + "@" + n.public_ipaddress } ), in: :groups, limit: 5, wait: 5 do |host|
    n = get_node_from_address(nodes, host.hostname)

    log_data, timestamp = start_env_exec( n.name, n.public_ipaddress, command, options, locs, passwords)
  end
end

#environment(type = "boot", treat_as_stateful = false, servers_to_interact_with = [], ask_on_destroy = false, remove = true, threads = []) ⇒ Object Also known as: e, env



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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/cheftacular/stateless_actions/environment.rb', line 37

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

  type = ARGV[1] if ARGV[1]

  servers_to_interact_with = ARGV[2].split(',') if ARGV[2]

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

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

  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"]

  unless servers_to_interact_with.empty?
    initial_servers = initial_servers.delete_if {|name, config_hash| !servers_to_interact_with.include?(name)}
  end

  if treat_as_stateful
    initial_servers = initial_servers.delete_if { |name, config_hash| config_hash.nil? || !config_hash.has_key?('descriptor') }

    initial_servers = initial_servers.delete_if { |name, config_hash| !config_hash['descriptor'].include?(@options['repository']) if !config_hash.nil? && config_hash.has_key?('descriptor') }
  end

  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|boot_without_deploy/
    begin
      initial_servers.each_pair do |name, config_hash|
        next if nodes.map { |n| n.name }.include?(name)
        config_hash ||= {}
        node_hash     = {}

        node_hash['node_name']   = name
        node_hash['flavor_name'] = config_hash.has_key?('flavor') ? config_hash['flavor'] : @config['cheftacular']['default_flavor_name']
        node_hash['descriptor']  = config_hash.has_key?('descriptor') ? config_hash['descriptor'] : name
        node_hash['dns_config']  = if config_hash.has_key?('dns_config')
                                     @config['parser'].parse_to_dns(config_hash['dns_config'], node_hash['node_name'])
                                   else
                                     @config['parser'].parse_to_dns('NODE_NAME.ENV_TLD', node_hash['node_name'])
                                   end

        @config['server_creation_queue'] << node_hash
      end

      @config['stateless_action'].cloud_bootstrap_from_queue
    end

    @config['ChefDataBag'].save_server_passwords_bag

    if type == 'boot'
      @options = @options.delete_if { |k,v| ['node_name','address', 'descriptor', 'with_dn', 'private_address', 'client_pass'].include?(k) }

      @options['role'] = @config['cheftacular']['backup_config']['db_primary_role']

      @options['unset_address_and_node_name'] = true

      database_host = @config['parser'].exclude_nodes( @config['getter'].get_true_node_objects(true), [{ unless: "role[#{ @options['role'] }]"}, { if: { not_env: @options['env'] } }], true).first

      unless database_host.nil?
        @config['action'].deploy

        backup_pid = Process.spawn("cft backups load --env=#{ @options['env'] }")
      end

      puts("NOTE! This command is not finished until your terminal returns to an input state!") unless database_host.nil?

      @options['role'] = 'all'

      @config['action'].deploy

      Process.wait(backup_pid) unless database_host.nil?

      puts "Done loading data and setting up #{ @options['env'] }!"
    end
  when 'destroy'        
    return false if ask_on_destroy && !environment_is_destroyable?

    @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
  when 'destroy_raw_servers'
    return false if ask_on_destroy && !environment_is_destroyable?

    @options['delete_server_on_remove'] = true

    initial_servers.each_pair do |name, config_hash|
      next if nodes.map { |n| n.name }.include?(name)
      @options['node_name'] = name

      real_node_name = @config['getter'].get_current_real_node_name

      @config['stateless_action'].cloud('servers', "destroy:#{ real_node_name }")

      sleep 15
    end
  end

  @config['auditor'].notify_slack_on_completion("environment #{ type } command completed for env: #{ @options['env'] } (#{ initial_servers.keys.join(' ,') })\n") if @config['cheftacular']['auditing']
end

#fedora_bootstrap_from_queue(out = []) ⇒ Object



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

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

#file(location = '', file_name = '', mode = '') ⇒ Object



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
# File 'lib/cheftacular/stateless_actions/file.rb', line 52

def file location='', file_name='',mode=''
  @options['node_name'] = ARGV[1] unless @options['node_name']
  location              = ARGV[2] if location.blank?
  mode                  = ARGV[3] if mode.blank?
  file_name             = ARGV[4] if file_name.blank?

  nodes = @config['error'].is_valid_node_name_option?

  mode = 'display' if mode.nil?
  mode = 'list'    if file_name.nil? || file_name.blank?

  interaction_mode = case mode.split(':').first
                     when 'cat'       then 'sshkit'
                     when 'display'   then 'sshkit'
                     when 'edit'      then 'ssh'
                     when /fetch|scp/ then 'scp'
                     when 'list'      then 'sshkit'
                     when 'tail'      then 'sshkit'
                     when 'tail-f'    then 'ssh'
                     else                  raise "Unsupported Mode for cft file: #{ mode }, "
                     end

  
  location = @config['parser'].parse_location_alias(location)
  command  = @config['parser'].parse_mode_into_command(mode)

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

  case interaction_mode
  when 'sshkit' then file_sshkit_execute(nodes, command, location, file_name)
  when 'ssh'    then file_ssh_execute(nodes, mode, command, location, file_name)
  when 'scp'    then file_scp_execute(nodes, command, location, file_name)
  end
end

#fix_known_hostsObject Also known as: fkh



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
# File 'lib/cheftacular/stateless_actions/fix_known_hosts.rb', line 24

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']
        arr << serv_hash['public']
      end
    end

    targets = arr.uniq
  end

  targets.each do |target|
    @config['filesystem'].scrub_from_known_hosts target
  end
end

#full_bootstrap_from_queue(options_to_sync = ['node_name', 'address', 'client_pass']) ⇒ Object



20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/cheftacular/stateless_actions/full_bootstrap_from_queue.rb', line 20

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

  case @config['cheftacular']['preferred_cloud_os']
  when 'ubuntu' || 'debian' then @config['stateless_action'].ubuntu_bootstrap_from_queue
  else                           @config['stateless_action'].instance_eval("#{ @config['cheftacular']['preferred_cloud_os'] }_bootstrap_from_queue")
  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_from_queue
end

#get_active_ssh_connections(connections_hash = {}, out = []) ⇒ Object



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

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

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

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

  on ( nodes.map { |n| @config['cheftacular']['deploy_user'] + "@" + n.public_ipaddress } ) do |host|
    n = get_node_from_address(nodes, host.hostname)

    puts "Beginning ssh connection check run for #{ n.name } (#{ n.public_ipaddress })"

    connections_hash[n.name] = start_connection_report( n.name, n.public_ipaddress, options, locs, passwords)
  end

  @config['filesystem'].generate_report_from_node_hash('connections report', connections_hash)
end

#get_haproxy_logObject



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

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| @config['cheftacular']['deploy_user'] + "@" + 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



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

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



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
# File 'lib/cheftacular/stateless_actions/get_pg_pass.rb', line 18

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

#help(inference_modes = []) ⇒ 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
82
# File 'lib/cheftacular/stateless_actions/help.rb', line 24

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 << 'short_context_descriptions'
  end

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

    puts @config['documentation']['action'][target_command.to_sym]['long_description'].flatten.join("\n\n")
  elsif @config['helper'].is_stateless_command? target_command
    @config['stateless_action_documentation'].send(target_command)

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

  if inference_modes.include?('action') || inference_modes.include?('both') || inference_modes.include?('short_context_descriptions')
    @config['action_documentation'].public_methods(false).each do |method|
      @config['action_documentation'].send(method)
    end
  end

  if inference_modes.include?('action') || inference_modes.include?('both') || inference_modes.include?('short_context_descriptions')
 
    @config['stateless_action_documentation'].public_methods(false).each do |method|
      @config['stateless_action_documentation'].send(method)
    end
  end

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

  if inference_modes.include?('short_context_descriptions')
    if @config['helper'].running_in_mode?('devops')
      puts @config['helper'].compile_short_context_descriptions(@config['documentation']['action'].merge(@config['documentation']['stateless_action']), 35)
    else
      puts @config['helper'].compile_short_context_descriptions(@config['documentation']['action'].merge(@config['documentation']['application']))
    end
  end
  
  puts @config['helper'].compile_documentation_lines(target_command).flatten.join("\n\n") if target_command =~ /action|stateless_action|application|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 'cft help #{ @config['cheftacular']['mode'] }' to see a list of commands"
  end
end

#initialize_cheftacular_yml(example_file_to_load = 'cheftacular.yml') ⇒ Object



29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/cheftacular/stateless_actions/initialize_cheftacular_yml.rb', line 29

def initialize_cheftacular_yml example_file_to_load='cheftacular.yml'
  example_file_to_load = 'thecheftacularcookbook.cheftacular.yml' if ARGV[1] == 'TheCheftacularCookbook'
  example_file_to_load = 'application.cheftacular.yml'            if ARGV[1] == 'application'

  FileUtils.mkdir_p(File.join(@config['locs']['chef-repo'], "config"))

  if File.exist?(File.join(@config['locs']['chef-repo'], "config", "cheftacular.yml"))
    @config['helper'].write_config_cheftacular_yml_file('cheftacular.example.yml', example_file_to_load )
  else
    @config['helper'].write_config_cheftacular_yml_file('cheftacular.yml', example_file_to_load)
  end
end

#initialize_data_bag_contents(env = "") ⇒ 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
# File 'lib/cheftacular/stateless_actions/initialize_data_bag_contents.rb', line 15

def initialize_data_bag_contents env=""
  raise "Environment #{ env } does not exist on chef server!" if !env.blank? && !@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

  @config['initializer'].initialize_environment_config_bag_contents if @config['helper'].running_in_mode?('devops')

  exit if @options['command'] == __method__
end

#knife_uploadObject Also known as: ku



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/cheftacular/stateless_actions/knife_upload.rb', line 24

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

  arg = ARGV[1]

  option = case arg
           when 'force' then ' --force'
           else ''
           end

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

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

  puts out
end

#list_toggleable_roles(possible_toggles = []) ⇒ Object



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
# File 'lib/cheftacular/stateless_actions/list_toggleable_roles.rb', line 17

def list_toggleable_roles possible_toggles=[]
  @options['node_name'] = ARGV[1] unless @options['node_name']

  raise "You have yet to fully configure your role toggling settings! Exiting..." if @config['cheftacular']['role_toggling'].has_key?('do_not_allow_toggling')

  nodes = @config['error'].is_valid_node_name_option?

  suffix = @config['cheftacular']['role_toggling']['deactivated_role_suffix']

  nodes.first.run_list.each do |role|
    role = role.gsub('role[','').gsub(']','')

    if !role.include?(suffix) && @config['parser'].parse_role("#{ role }#{ suffix }", 'boolean')
      possible_toggles << role
      possible_toggles << "#{ role }#{ suffix }"

    elsif role.include?(suffix) && @config['parser'].parse_role("#{ role.gsub(suffix,'') }", 'boolean')
      possible_toggles << role
      possible_toggles << role.gsub(suffix,'')
    end
  end

  puts "The current run_list for #{ @options['node_name'] } is:"

  ap nodes.first.run_list

  puts "\nThe possible toggles for #{ @options['node_name'] } are:"

  ap possible_toggles
end

#location_aliasesObject Also known as: la



27
28
29
# File 'lib/cheftacular/stateless_actions/location_aliases.rb', line 27

def location_aliases
  ap @config['cheftacular']['location_aliases']
end

#pass(node_name = '', mode = 'normal') ⇒ Object



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 18

def pass node_name='', mode='normal'
  @options['node_name'] = ARGV[1] if !@options['node_name'] && node_name.blank?
  @options['node_name'] = node_name if !@options['node_name'] && !node_name.blank?

  nodes = @config['error'].is_valid_node_name_option?

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

  if mode =~ /normal/
    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"] }"
  end

  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 if mode =~ /normal/
end

#redhat_bootstrap_from_queue(out = []) ⇒ Object



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

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

#reinitialize(out = []) ⇒ 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
# File 'lib/cheftacular/stateless_actions/reinitialize.rb', line 16

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"`

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

  out << `#{ knife_bootstrap_command }`

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

#remove_client(delete_server = false, remove = true) ⇒ Object Also known as: remove_node, rc



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
# File 'lib/cheftacular/stateless_actions/remove_client.rb', line 24

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

  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:#{ @config['getter'].get_current_real_node_name(n.name) }"
      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
  end
  
  puts("Done. Please verify that the output of the next line(s) match your expectations (running cft client_list)") if @options['verbose']
  
  @config['stateless_actions'].client_list if @options['verbose']
end

#replication_status(rep_status_hash = {}, out = []) ⇒ 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
# File 'lib/cheftacular/stateless_actions/replication_status.rb', line 16

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| @config['cheftacular']['deploy_user'] + "@" + 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| @config['cheftacular']['deploy_user'] + "@" + 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

#reset_bag(bag_name = '', bag_env = '') ⇒ Object



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

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

  bag_name = ARGV[1]         if bag_name.blank?
  bag_env  = @options['env'] if bag_env.blank?

  begin
    @config['ChefDataBag'].send("reset_#{ bag_name }_bag")

    puts "Successfully reset bag #{ bag_name }."
  rescue NoMethodError => e
    puts "You are not able to reset the bag \"#{ bag_name }\" via reset_bag, please use knife to edit the contents.\n #{ e }"
  end

  @config['stateless_action'].clear_caches
end

#restart_swapObject



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

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| @config['cheftacular']['deploy_user'] + "@" + 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

#role_toggle(state_toggle = '', target_run_list = [], skip_confirm = false) ⇒ Object Also known as: rt



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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/cheftacular/stateless_actions/role_toggle.rb', line 41

def role_toggle state_toggle='', target_run_list=[], skip_confirm=false
  @options['node_name'] = ARGV[1] unless @options['node_name']
  @config['parser'].parse_role(ARGV[2])
  state_toggle          = ARGV[3] if state_toggle.blank?

  raise "You have yet to fully configure your role toggling settings! Exiting..." if @config['cheftacular']['role_toggling'].has_key?('do_not_allow_toggling')
  raise "You may only enter activate or deactivate for the state toggle argument for the #{ __method__ } command." unless (state_toggle =~ /activate|deactivate/) == 0

  @config['initializer'].initialize_data_bags_for_environment @options['env'], false, ['node_roles']

  @config['initializer'].initialize_node_roles_bag_contents @options['env']

  @config['filesystem'].cleanup_file_caches('current-nodes')

  nodes = @config['error'].is_valid_node_name_option?

  suffix = @config['cheftacular']['role_toggling']['deactivated_role_suffix']

  if @options['role'].include?(suffix)
    unless @config['parser'].parse_role("#{ @options['role'].gsub(suffix,'') }", 'boolean')
      puts "Role #{ @options['role'] } does not have an activated role! There is no #{ @options['role'].gsub(suffix,'') } role!"

      return false
    end
  else
    unless @config['parser'].parse_role("#{ @options['role'] }#{ suffix }", 'boolean')
      puts "Role #{ @options['role'] } does not have a deactivated role! There is no #{ @options['role'] }#{ suffix } role!"

      return false
    end
  end

  current_node_roles = nodes.first.run_list

  if current_node_roles.include?("role[#{ @options['role'] }]") && !@options['role'].include?(suffix)
    if state_toggle == 'activate'
      puts "The role #{ @options['role'] } is already activated for #{ nodes.first.name }!"
    else
      puts "The role #{ @options['role'] } is currently activated, setting it to #{ @options['role'] }#{ suffix }"

      target_run_list = current_node_roles.map {|r| r.gsub(@options['role'], "#{ @options['role'] }#{ suffix }") if current_node_roles.include?("role[#{ @options['role'] }]") }
    end
  elsif current_node_roles.include?("role[#{ @options['role'] }]") && @options['role'].include?(suffix)
    if state_toggle == 'activate'
      puts "The role #{ @options['role'] } is currently deactivated, setting it to #{ @options['role'].gsub(suffix, '') }"

      target_run_list = current_node_roles.map {|r| r.gsub(@options['role'], "#{ @options['role'].gsub(suffix, '') }") if current_node_roles.include?("role[#{ @options['role'] }]") }
    else
      puts "The role #{ @options['role'] } is already deactivated for #{ nodes.first.name }!"
    end
  elsif current_node_roles.include?("role[#{ @options['role'] }#{ suffix }]") #they passed in the reverse of a role that was already deactivated
    if state_toggle == 'activate'
      puts "The role #{ @options['role'] } is currently deactivated, setting it to it's activated state"

      target_run_list = current_node_roles.map {|r| r.gsub("#{ @options['role'] }#{ suffix }", "#{ @options['role'] }") if current_node_roles.include?("role[#{ @options['role'] }#{ suffix }]") }
    else
      puts "The role #{ @options['role'] } is already deactivated for #{ nodes.first.name }!"
    end
  elsif current_node_roles.include?("role[#{ @options['role'].gsub(suffix, '') }]") #they passed in the reverse of a role that was already activated
    if state_toggle == 'activate'
      puts "The role #{ @options['role'] } is already activated for #{ nodes.first.name }!"
    else
      puts "The role #{ @options['role'] } is currently activated, setting it to it's deactivated state"
      
      target_run_list = current_node_roles.map {|r| r.gsub("#{ @options['role'].gsub('suffix','') }", "#{ @options['role'] }") if current_node_roles.include?("role[#{ @options['role'].gsub('suffix','') }]") }
    end
  elsif !current_node_roles.include?("role[#{ @options['role'] }]") && @config['cheftacular']['role_toggling']['strict_roles']
    puts "The node does not have #{ @options['role'] } and strict roles is set to true, exiting..."

    return false
  elsif !current_node_roles.include?("role[#{ @options['role'] }]") && !@config['cheftacular']['role_toggling']['strict_roles']
    puts "The node does not have #{ @options['role'] } and strict roles is set to false, setting this new role..."

    target_run_list = current_node_roles + "role[#{ @options['role'] }"
  end

  unless target_run_list.empty?
    puts "Updating node run list for #{ nodes.first.name } from"

    ap current_node_roles

    puts "to:"

    ap target_run_list

    if skip_confirm || !@config['cheftacular']['role_toggling']['skip_confirm']
      puts "Enter Y/y to confirm."

      input = STDIN.gets.chomp

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

    @config[@options['env']]['node_roles_bag_hash']['node_roles'][nodes.first.name.gsub(/\d/,'')]['run_list'] = target_run_list

    nodes.first.send("run_list=", target_run_list)

    nodes.first.save

    @config['ChefDataBag'].save_node_roles_bag @options['env']

    @config['filesystem'].cleanup_file_caches('current-nodes')

    puts "Triggering deploy to set the new role..."

    @config['action'].deploy
  end
end

#rvm(command = '') ⇒ Object



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
# File 'lib/cheftacular/stateless_actions/rvm.rb', line 41

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| @config['cheftacular']['deploy_user'] + "@" + 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



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/server_update.rb', line 22

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| @config['cheftacular']['deploy_user'] + "@" + 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| @config['cheftacular']['deploy_user'] + "@" + 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



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/service.rb', line 27

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 'ls /etc/init'
            when /list/             then 'ls /etc/init'
            when 'restart'          then "service #{ ARGV[2] } restart"
            when 'stop'             then "service #{ ARGV[2] } stop"
            when 'start'            then "service #{ ARGV[2] } start"
            else                         'ls /etc/init'
            end

  command = case ARGV[1]
            when nil                then 'ls /etc/init.d'
            when /list/             then 'ls /etc/init.d'
            when 'restart'          then "sv restart #{ ARGV[2] }"
            when 'stop'             then "sv stop #{ ARGV[2] }"
            when 'start'            then "sv start #{ ARGV[2] }"
            else                         'ls /etc/init.d'
            end if @options['runsv']

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

  #initctl list | awk '{ print $1 }' | xargs -n1 initctl show-config
  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| @config['cheftacular']['deploy_user'] + "@" + n.public_ipaddress } ) do |host|
    n = get_node_from_address(nodes, host.hostname)

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

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

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



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

def slack message='', channel=''
  return false if @config['cheftacular']['slack']['webhook'].nil?

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

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

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

#ssh(node_name = '', command = '') ⇒ Object



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/ssh.rb', line 22

def ssh node_name='', command=''
  @options['node_name'] = ARGV[1] unless @options['node_name']

  command = ARGV[3] if command.blank? && ARGV[2] == 'exec' 

  @config['stateless_action'].pass(@options['node_name']) if command.blank?

  nodes = @config['error'].is_valid_node_name_option?

  if !command.blank? && nodes.first.chef_environment != @options['env']
    @config['initializer'].initialize_data_bags_for_environment nodes.first.chef_environment, false, ['addresses', 'server_passwords']

    @config['initializer'].initialize_passwords nodes.first.chef_environment
  end

  nodes.each do |n|
    puts("Beginning ssh run for #{ n.name } (#{ n.public_ipaddress })") unless @options['quiet']

    start_ssh_session(n.public_ipaddress, command)
  end

  @config['auditor'].notify_slack_on_completion("ssh run completed on #{ @options['node_name'] } (#{ nodes.first.public_ipaddress })\n") if @config['cheftacular']['auditing'] && command.blank?
end

#test_env(split_env = "splitstaging", type = "boot") ⇒ 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
# File 'lib/cheftacular/stateless_actions/test_env.rb', line 27

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'
    begin
      @config['cheftacular']['split_env_nodes'].each_pair do |name, config_hash|                                    
        next if nodes.map { |n| n.name }.include?(@options['node_name'])
        config_hash ||= {}
        node_hash     = {}
        true_name                = name.gsub('SPLITENV', split_env)
        @options['sub_env']      = split_env
        node_hash['node_name']   = name
        node_hash['flavor_name'] = config_hash.has_key?('flavor') ? config_hash['flavor'] : @config['cheftacular']['default_flavor_name']
        node_hash['descriptor']  = config_hash.has_key?('descriptor') ? "#{ config_hash['descriptor'] }-#{ split_env }" : name
        node_hash['dns_config']  = if config_hash.has_key?('dns_config')
                                     @config['parser'].parse_to_dns(config_hash['dns_config'], node_hash['node_name'])
                                   else
                                     @config['parser'].parse_to_dns('NODE_NAME.ENV_TLD', node_hash['node_name'])
                                   end

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

        @config['stateless_action'].cloud_bootstrap_from_queue

        sleep 15
      end
    ensure
      @config['ChefDataBag'].save_server_passwords_bag
    end
  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_from_queue(threads = [], execution_hash_array = []) ⇒ 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
# File 'lib/cheftacular/stateless_actions/bootstrappers/ubuntu_bootstrap_from_queue.rb', line 16

def ubuntu_bootstrap_from_queue threads=[], execution_hash_array=[]
  raise "This action is not meant to be called directly!" if !@options['in_scaling'] && !@options['in_single_server_creation']

  @config['bootstrap_timestamp'] ||= Time.now.strftime("%Y%m%d%H%M%S")

  @config['queue_master'].generate_passwords_for_each_server_hash_in_queue

  execution_hash_array << compile_root_execute_hash
  execution_hash_array << compile_deploy_execute_hash
  execution_hash_array << compile_rvm_execute_hash           if @config['cheftacular']['install_rvm_on_boot']
  execution_hash_array << compile_install_rvm_sh_file_hashes if @config['cheftacular']['install_rvm_on_boot']
  execution_hash_array  = execution_hash_array.flatten(1)

  @config['server_creation_queue'].each do |server_hash|
    puts("#{ server_name_output(server_hash) }_Starting initial setup for server...")

    threads << Thread.new { execute_execution_hash_array(server_hash, execution_hash_array) }
  end

  threads.each { |thread| thread.join }

  @config['server_creation_queue'].each do |server_hash|
    @config[@options['env']]['server_passwords_bag_hash']["#{ server_hash['address'] }-root-pass"]   = server_hash['client_pass']
    @config[@options['env']]['server_passwords_bag_hash']["#{ server_hash['address'] }-deploy-pass"] = server_hash['deploy_password']
    @config[@options['env']]['server_passwords_bag_hash']["#{ server_hash['address'] }-name"]        = server_hash['node_name']
  end

  @config['ChefDataBag'].save_server_passwords_bag unless @config['in_server_creation_queue']
end

#update_chef_clientObject



14
15
16
17
# File 'lib/cheftacular/stateless_actions/update_chef_client.rb', line 14

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

#update_cheftacular(status_hash = {}) ⇒ Object



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

def update_cheftacular status_hash={}
  update_cheftacular_not_configured_to_update if @config['cheftacular']['self_update_repository'] != @config['locs']['root'].split('/').last

  @config['helper'].set_detected_cheftacular_version

  puts "Attempting to update cheftacular from #{ Cheftacular::VERSION } to #{ @config['detected_cheftacular_version'] }..."

  status_hash['gemfile_is_latest_version'] = update_cheftacular_from_git
  status_hash['gemfile_is_latest_version'] = update_cheftacular_gemfile unless status_hash['gemfile_is_latest_version']
  #status_hash['bundled_latest_version']    = update_cheftacular_bundle  if status_hash['gemfile_is_latest_version']

  puts "Please run bundle install to update to the latest version."

  #if !status_hash['gemfile_is_latest_version'] || !status_hash['bundled_latest_version']
  #  puts(
  #    "Issues occured in automatically updating your cheftacular " +
  #    "to #{ @config['detected_cheftacular_version'] }, please send " +
  #    "the output of this command to your DevOps administrator or add " +
  #    "it as an issue at this gem's github page."
  #  )
  #else
  #  puts "Successfully installed version #{ @config['detected_cheftacular_version'] }, please re-run your command."
  #end
end

#update_cloudflare_dns_from_cloudObject



26
27
28
29
30
31
32
33
34
35
36
# File 'lib/cheftacular/stateless_actions/update_cloudflare_dns_from_cloud.rb', line 26

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_cookbook(cookbook = 'TheCheftacularCookbook', version = 'latest') ⇒ Object Also known as: uc



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_cookbook.rb', line 29

def update_cookbook cookbook='TheCheftacularCookbook', version='latest'
  raise "This action can only be performed if the mode is set to devops" unless @config['helper'].running_in_mode?('devops')

  cookbook = ARGV[1] if ARGV[1]
  version  = ARGV[2] if ARGV[2]

  if version == 'local' 
    if File.exists?(File.expand_path("#{ @config['locs']['true-root'] }/#{ cookbook }"))
      `rm -Rf #{ @config['locs']['cookbooks'] }/#{ cookbook }` if File.exists?(File.expand_path("#{ @config['locs']['cookbooks'] }/#{ cookbook }"))
      `cp -Rf #{ @config['locs']['true-root'] }/#{ cookbook } #{ @config['locs']['cookbooks'] }/#{ cookbook }`

      `rm -Rf #{ @config['locs']['cookbooks'] }/#{ cookbook }/.git`
    else
      puts "You do not have #{ cookbook } under the #{ @config['locs']['true-root'] } directory!"
    end
  else
    @config['cheftacular']['wrapper_cookbooks'].split(',').each do |wrapper_cookbook|
      wrapper_cookbook_loc = "#{ @config['locs']['cookbooks'] }/#{ wrapper_cookbook }"
      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 fetching cookbooks, moving #{ cookbook } into local chef repo"

      specific_cookbook = @config['filesystem'].parse_berkshelf_cookbook_versions(version).select {|key| key.include?(cookbook)}[cookbook]

      puts "Moving #{ cookbook } (#{ specific_cookbook['version'] })[#{ specific_cookbook['mtime'] }] to your chef-repo!"

      `rm -Rf #{ @config['locs']['cookbooks'] }/#{ cookbook }` if File.exists?(File.expand_path("#{ @config['locs']['cookbooks'] }/#{ cookbook }"))
      `cp -Rf #{ @config['locs']['berks'] }/#{ specific_cookbook['location'] } #{ @config['locs']['cookbooks'] }/#{ cookbook }`
      
      break
    end
  end
end

#update_split_branchesObject



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
# File 'lib/cheftacular/stateless_actions/update_split_branches.rb', line 28

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_the_cheftacular_cookbook_and_knife_uploadObject Also known as: utccaku, utcc



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

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

  @config['stateless_action'].update_cookbook('TheCheftacularCookbook', 'local')

  @config['stateless_action'].knife_upload
end

#update_tld(target_tld = "") ⇒ Object



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
# File 'lib/cheftacular/stateless_actions/update_tld.rb', line 17

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']

  target_tld = old_tld if target_tld == 'self'

  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

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

#upload_nodes(invalidate_file_node_cache = true) ⇒ Object Also known as: un



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
131
132
133
134
135
136
137
# File 'lib/cheftacular/stateless_actions/upload_nodes.rb', line 32

def upload_nodes invalidate_file_node_cache=true
  @config['filesystem'].cleanup_file_caches('current-nodes') if invalidate_file_node_cache

  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) : @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['filesystem'].is_junk_filename?(fr)

    Dir.foreach("#{ @config['locs']['nodes'] }/#{ fr }") do |f|
      next if @config['filesystem'].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['filesystem'].cleanup_file_caches('current-nodes') if invalidate_file_node_cache
end

#upload_rolesObject Also known as: ur



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

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['filesystem'].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

#versionObject Also known as: v



27
28
29
# File 'lib/cheftacular/stateless_actions/version.rb', line 27

def version
  @config['helper'].display_currently_installed_version
end

#vyatta_bootstrap_from_queue(out = []) ⇒ Object



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

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