cheftacular 2.6.0 → 2.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/cheftacular/README.md +104 -64
- data/lib/cheftacular/actions/tail.rb +17 -10
- data/lib/cheftacular/chef/data_bag.rb +49 -9
- data/lib/cheftacular/cheftacular.rb +6 -2
- data/lib/cheftacular/file_system.rb +26 -5
- data/lib/cheftacular/getter.rb +7 -0
- data/lib/cheftacular/helper.rb +29 -0
- data/lib/cheftacular/initializer.rb +43 -2
- data/lib/cheftacular/parser.rb +4 -8
- data/lib/cheftacular/queue_master.rb +16 -0
- data/lib/cheftacular/stateless_actions/backups.rb +169 -58
- data/lib/cheftacular/stateless_actions/check_cheftacular_yml_keys.rb +9 -0
- data/lib/cheftacular/stateless_actions/cheftacular_config.rb +51 -0
- data/lib/cheftacular/stateless_actions/clear_caches.rb +24 -0
- data/lib/cheftacular/stateless_actions/client_list.rb +1 -1
- data/lib/cheftacular/stateless_actions/create_git_key.rb +2 -1
- data/lib/cheftacular/stateless_actions/fix_known_hosts.rb +1 -1
- data/lib/cheftacular/stateless_actions/initialize_cheftacular_yml.rb +16 -6
- data/lib/cheftacular/stateless_actions/initialize_data_bag_contents.rb +43 -0
- data/lib/cheftacular/stateless_actions/reset_bag.rb +30 -0
- data/lib/cheftacular/stateless_actions/role_toggle.rb +1 -1
- data/lib/cheftacular/stateless_actions/update_tld.rb +1 -3
- data/lib/cheftacular/version.rb +1 -1
- metadata +6 -2
@@ -39,9 +39,11 @@ class Cheftacular
|
|
39
39
|
if @config['helper'].is_initialization_command?(ARGV[0])
|
40
40
|
@options['command'] = ARGV[0] #this is normally set by parse_context but that is not run for initialization commands
|
41
41
|
else
|
42
|
+
@config['stateless_action'].cheftacular_config('sync') unless @config['helper'].running_on_chef_node?
|
43
|
+
|
42
44
|
@config['stateless_action'].initialize_data_bag_contents(@options['env']) #ensure basic structure are always maintained before each run
|
43
45
|
|
44
|
-
@config['parser'].parse_application_context if @config['
|
46
|
+
@config['parser'].parse_application_context if @config['helper'].running_in_mode?('application')
|
45
47
|
|
46
48
|
@config['parser'].parse_context
|
47
49
|
|
@@ -50,7 +52,7 @@ class Cheftacular
|
|
50
52
|
@config['auditor'].audit_run if @config['cheftacular']['auditing']
|
51
53
|
end
|
52
54
|
|
53
|
-
@config['stateless_action'].
|
55
|
+
@config['stateless_action'].check_cheftacular_yml_keys unless @config['helper'].is_initialization_command?(ARGV[0])
|
54
56
|
|
55
57
|
@config['action'].send(@options['command']) if @config['helper'].is_command?(@options['command'])
|
56
58
|
|
@@ -58,6 +60,8 @@ class Cheftacular
|
|
58
60
|
|
59
61
|
@config['stateless_action'].send('help') if @config['helper'].is_not_command_or_stateless_command?(@options['command'])
|
60
62
|
|
63
|
+
@config['queue_master'].work_off_slack_queue unless @config['helper'].is_initialization_command?(@options['command'])
|
64
|
+
|
61
65
|
@config['helper'].output_run_stats
|
62
66
|
|
63
67
|
exit #explicitly call this in case some celluoid workers are still hanging around
|
@@ -14,6 +14,10 @@ class Cheftacular
|
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
+
def write_environment_config_cache
|
18
|
+
File.open( current_environment_config_cache_file_path, "w") { |f| f.write("set for #{ Time.now.strftime("%Y%m%d") }") }
|
19
|
+
end
|
20
|
+
|
17
21
|
def check_nodes_file_cache nodes=[]
|
18
22
|
Dir.entries(current_nodes_file_cache_path).each do |location|
|
19
23
|
next if is_junk_filename?(location)
|
@@ -32,6 +36,10 @@ class Cheftacular
|
|
32
36
|
current_file_path 'audit-check.txt'
|
33
37
|
end
|
34
38
|
|
39
|
+
def current_environment_config_cache_file_path
|
40
|
+
current_file_path 'environment_config-check.txt'
|
41
|
+
end
|
42
|
+
|
35
43
|
def is_junk_filename? filename
|
36
44
|
filename =~ /.DS_Store|.com.apple.timemachine.supported|README.*/ || filename == '.' || filename == '..' && File.directory?(filename)
|
37
45
|
end
|
@@ -68,12 +76,17 @@ class Cheftacular
|
|
68
76
|
|
69
77
|
Dir.entries(base_dir).each do |entry|
|
70
78
|
next if is_junk_filename?(entry)
|
79
|
+
next if File.file?("#{ base_dir }/#{ entry }") && entry == 'local_cheftacular_cache' && mode != 'all'
|
71
80
|
|
72
81
|
case mode
|
73
82
|
when 'old'
|
74
83
|
FileUtils.rm("#{ base_dir }/#{ entry }") if File.file?("#{ base_dir }/#{ entry }") && !entry.include?(Time.now.strftime("%Y%m%d"))
|
75
|
-
when 'current'
|
84
|
+
when 'current-nodes'
|
76
85
|
check_current_day_entry = true
|
86
|
+
when 'all'
|
87
|
+
FileUtils.rm("#{ base_dir }/#{ entry }") if File.file?("#{ base_dir }/#{ entry }")
|
88
|
+
|
89
|
+
FileUtils.rm_rf("#{ base_dir }/#{ entry }") if File.exists?("#{ base_dir }/#{ entry }") && File.directory?("#{ base_dir }/#{ entry }")
|
77
90
|
when 'current-audit-only'
|
78
91
|
FileUtils.rm("#{ base_dir }/#{ entry }") if File.file?("#{ base_dir }/#{ entry }") && entry.include?(Time.now.strftime("%Y%m%d"))
|
79
92
|
end
|
@@ -102,21 +115,29 @@ class Cheftacular
|
|
102
115
|
current_file_path "chef_repo_cheftacular_cache"
|
103
116
|
end
|
104
117
|
|
118
|
+
def local_cheftacular_file_cache_path
|
119
|
+
current_file_path "local_cheftacular_cache", false
|
120
|
+
end
|
121
|
+
|
105
122
|
def write_chef_repo_cheftacular_cache_file hash
|
106
123
|
File.open( current_chef_repo_cheftacular_file_cache_path, "w") { |f| f.write(hash) }
|
107
124
|
end
|
108
125
|
|
126
|
+
def write_local_cheftacular_cache_file hash_string
|
127
|
+
File.open( local_cheftacular_file_cache_path, 'w') { |f| f.write(hash_string) }
|
128
|
+
end
|
129
|
+
|
109
130
|
def write_chef_repo_cheftacular_yml_file file_location
|
110
131
|
File.open( file_location, "w") { |f| f.write(@config['helper'].compile_chef_repo_cheftacular_yml_as_hash.to_yaml) }
|
111
132
|
end
|
112
133
|
|
113
|
-
def write_config_cheftacular_yml_file
|
114
|
-
File.open( File.join(@config['locs']['chef-repo'], "config",
|
134
|
+
def write_config_cheftacular_yml_file to_be_created_filename='cheftacular.yml', example_filename='cheftacular.yml'
|
135
|
+
File.open( File.join(@config['locs']['chef-repo'], "config", to_be_created_filename), "w") { |f| f.write(File.read(File.join(@config['locs']['examples'], example_filename))) }
|
115
136
|
end
|
116
137
|
|
117
138
|
private
|
118
|
-
def current_file_path file_name
|
119
|
-
File.join( @config['locs']['app-root'], 'tmp', @config['helper'].declassify, "#{ Time.now.strftime("%Y%m%d") }-#{ file_name }")
|
139
|
+
def current_file_path file_name, use_timestamp=true
|
140
|
+
File.join( @config['locs']['app-root'], 'tmp', @config['helper'].declassify, ( use_timestamp ? "#{ Time.now.strftime("%Y%m%d") }-#{ file_name }" : file_name ))
|
120
141
|
end
|
121
142
|
end
|
122
143
|
end
|
data/lib/cheftacular/getter.rb
CHANGED
@@ -120,6 +120,13 @@ class Cheftacular
|
|
120
120
|
ret_hash
|
121
121
|
end
|
122
122
|
|
123
|
+
def get_db_primary_node
|
124
|
+
nodes = get_true_node_objects true
|
125
|
+
target_db_role = get_current_repo_config['db_primary_host_role']
|
126
|
+
|
127
|
+
@config['parser'].exclude_nodes( nodes, [{ unless: "role[#{ target_db_role }]" }, { if: { not_env: @options['env'] } }], true)
|
128
|
+
end
|
129
|
+
|
123
130
|
def get_split_branch_hash ret={}
|
124
131
|
@config['cheftacular']['repositories'].each_pair do |name, repo_hash|
|
125
132
|
ret[repo_hash['name']] = repo_hash if repo_hash.has_key?('has_split_branches') && repo_hash['has_split_branches']
|
data/lib/cheftacular/helper.rb
CHANGED
@@ -263,6 +263,35 @@ class Cheftacular
|
|
263
263
|
end
|
264
264
|
end
|
265
265
|
|
266
|
+
class Hash
|
267
|
+
def deep_diff(compare_hash, remove_if_nil_on_original=false)
|
268
|
+
original_hash = self
|
269
|
+
|
270
|
+
(original_hash.keys | compare_hash.keys).inject({}) do |diff_hash, key|
|
271
|
+
if original_hash[key] != compare_hash[key]
|
272
|
+
if original_hash[key].respond_to?(:deep_diff) && compare_hash[key].respond_to?(:deep_diff)
|
273
|
+
diff_hash[key] = original_hash[key].deep_diff(compare_hash[key], remove_if_nil_on_original)
|
274
|
+
else
|
275
|
+
if remove_if_nil_on_original
|
276
|
+
diff_hash[key] = []
|
277
|
+
diff_hash[key] << original_hash[key] if original_hash.has_key?(key)
|
278
|
+
diff_hash[key] << compare_hash[key] if original_hash.has_key?(key)
|
279
|
+
diff_hash.delete(key) if diff_hash[key].empty?
|
280
|
+
else
|
281
|
+
diff_hash[key] = [original_hash[key], compare_hash[key]]
|
282
|
+
end
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
diff_hash
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
def compact
|
291
|
+
self.select { |_, value| !value.nil? }
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
266
295
|
class String
|
267
296
|
def scrub_pretty_text
|
268
297
|
self.gsub("",'').gsub(/\[0m|\[1m|\[32m|\[35m|\[36m/,'')
|
@@ -4,12 +4,16 @@ class Cheftacular
|
|
4
4
|
def initialize options, config
|
5
5
|
@options, @config = options, config
|
6
6
|
|
7
|
+
initialize_queues
|
8
|
+
|
7
9
|
initialize_yaml_configuration
|
8
10
|
|
9
11
|
initialize_default_cheftacular_options
|
10
12
|
|
11
13
|
initialize_locations
|
12
14
|
|
15
|
+
initialize_data_bag_cheftacular_hash if !@config['helper'].is_initialization_command?(ARGV[0]) && !@config['helper'].running_on_chef_node?
|
16
|
+
|
13
17
|
initialize_monkeypatches unless @config['helper'].running_on_chef_node?
|
14
18
|
|
15
19
|
initialize_arguments
|
@@ -231,13 +235,45 @@ class Cheftacular
|
|
231
235
|
end.parse!
|
232
236
|
end
|
233
237
|
|
238
|
+
def initialize_queues
|
239
|
+
@config['slack_queue'] ||= []
|
240
|
+
end
|
241
|
+
|
234
242
|
def initialize_yaml_configuration
|
235
243
|
@config['cheftacular'] = @config['helper'].get_cheftacular_yml_as_hash
|
236
244
|
end
|
237
245
|
|
246
|
+
def initialize_data_bag_cheftacular_hash
|
247
|
+
initialize_ridley
|
248
|
+
|
249
|
+
@config['ChefDataBag'] ||= Cheftacular::ChefDataBag.new(@options, @config)
|
250
|
+
|
251
|
+
@config['ChefDataBag'].init_bag('default', 'cheftacular', false)
|
252
|
+
|
253
|
+
diff_hash = @config['cheftacular'].deep_diff(@config['default']['cheftacular_bag_hash'], true).except('mode', 'default_repository').compact
|
254
|
+
|
255
|
+
diff_hash.each_pair do |key, value|
|
256
|
+
diff_hash.delete(key) if value.empty? || value.nil?
|
257
|
+
end
|
258
|
+
|
259
|
+
if @config['helper'].running_in_mode?('devops') && !diff_hash.empty?
|
260
|
+
puts "Difference detected between local cheftacular.yml and data bag cheftacular.yml! Displaying..."
|
261
|
+
|
262
|
+
ap diff_hash
|
263
|
+
elsif @config['helper'].running_in_mode?('application') && @config['default']['cheftacular_bag_hash']['slack']['webhook'] && !diff_hash.empty?
|
264
|
+
@config['slack_queue'] << diff_hash.awesome_inspect({plain: true, indent: 2}).prepend('```').insert(-1, '```')
|
265
|
+
end
|
266
|
+
|
267
|
+
@config['cheftacular'] = if @config['default']['cheftacular_bag_hash']['sync_application_cheftacular_yml']
|
268
|
+
@config['default']['cheftacular_bag_hash'].deep_merge(@config['cheftacular'])
|
269
|
+
else
|
270
|
+
@config['cheftacular'].deep_merge(@config['default']['cheftacular_bag_hash'].except('default_repository', 'mode'))
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
238
274
|
def initialize_default_cheftacular_options
|
239
275
|
@options['env'] = @config['cheftacular']['default_environment'] if @config['cheftacular'].has_key?('default_environment')
|
240
|
-
@options['repository'] = @config['cheftacular']['default_repository']
|
276
|
+
@options['repository'] = @config['cheftacular']['default_repository'] if @config['cheftacular'].has_key?('default_repository')
|
241
277
|
end
|
242
278
|
|
243
279
|
def initialize_monkeypatches
|
@@ -287,7 +323,7 @@ class Cheftacular
|
|
287
323
|
locs['chef'] = File.expand_path("~/.chef") unless locs['chef']
|
288
324
|
locs['cookbooks'] = File.expand_path("#{ locs['chef-repo'] }/cookbooks")
|
289
325
|
locs['berks'] = File.expand_path('~/.berkshelf/cookbooks')
|
290
|
-
locs['wrapper-cookbooks'] = @config['cheftacular']['wrapper_cookbooks']
|
326
|
+
locs['wrapper-cookbooks'] = @config['cheftacular']['wrapper_cookbooks'] unless @config['helper'].running_in_mode?('application')
|
291
327
|
locs['ssh'] = File.expand_path('~/.ssh')
|
292
328
|
locs['chef-log'] = File.expand_path("#{ locs['root']}/log") unless locs['chef-log']
|
293
329
|
locs['app-tmp'] = File.expand_path("#{ locs['app-root']}/tmp")
|
@@ -330,6 +366,10 @@ class Cheftacular
|
|
330
366
|
|
331
367
|
@config['ChefDataBag'].init_bag('default', 'authentication') if bags_to_load.empty? || bags_to_load.include?('authentication')
|
332
368
|
|
369
|
+
@config['ChefDataBag'].init_bag('default', 'cheftacular', false) if bags_to_load.include?('cheftacular')
|
370
|
+
|
371
|
+
@config['ChefDataBag'].init_bag('default', 'environment_config', false) if bags_to_load.empty? || bags_to_load.include?('environment_config')
|
372
|
+
|
333
373
|
@config['helper'].completion_rate?(38, 'initializer') if in_initializer
|
334
374
|
|
335
375
|
@config['ChefDataBag'].init_bag(env, 'addresses', false) if bags_to_load.empty? || bags_to_load.include?('addresses')
|
@@ -437,6 +477,7 @@ class Cheftacular
|
|
437
477
|
@config['error'] = Cheftacular::Error.new(@options, @config)
|
438
478
|
@config['dummy_sshkit'] = SSHKit::Backend::Netssh.new(SSHKit::Host.new('127.0.0.1'))
|
439
479
|
@config['DNS'] = Cheftacular::DNS.new(@options, @config)
|
480
|
+
@config['queue_master'] = Cheftacular::QueueMaster.new(@options, @config)
|
440
481
|
@config['cloud_provider'] = Cheftacular::CloudProvider.new(@options, @config)
|
441
482
|
end
|
442
483
|
|
data/lib/cheftacular/parser.rb
CHANGED
@@ -15,11 +15,7 @@ class Cheftacular
|
|
15
15
|
|
16
16
|
@options['command'] = ARGV[0] unless @options['command']
|
17
17
|
|
18
|
-
|
19
|
-
parse_repository(@options['repository'])
|
20
|
-
|
21
|
-
parse_role(@options['role'])
|
22
|
-
end
|
18
|
+
parse_repository(@options['repository'])
|
23
19
|
|
24
20
|
parse_node_name(@options['node_name']) if @options['node_name']
|
25
21
|
|
@@ -45,7 +41,7 @@ class Cheftacular
|
|
45
41
|
@options['command'] = ARGV[0] unless @config['helper'].is_not_command_or_stateless_command?(ARGV[0])
|
46
42
|
end
|
47
43
|
|
48
|
-
return if !@options['
|
44
|
+
return if !@options['repository'].nil? && !@options['role'].nil? && !@options['command'].nil?
|
49
45
|
return if !@options['command'].nil? && @config['helper'].is_stateless_command?(ARGV[0])
|
50
46
|
end
|
51
47
|
|
@@ -107,10 +103,10 @@ class Cheftacular
|
|
107
103
|
raise "Cannot set or unset target_revision without a role" unless @options['role']
|
108
104
|
|
109
105
|
if @options['target_revision']
|
110
|
-
@config[@options['env']]['config_bag_hash'][@options['sub_env']]['app_revisions'][@config['getter'].
|
106
|
+
@config[@options['env']]['config_bag_hash'][@options['sub_env']]['app_revisions'][@config['getter'].get_repository_from_role_name(@options['role'])] = @options['target_revision']
|
111
107
|
|
112
108
|
elsif @options['unset_revision']
|
113
|
-
@config[@options['env']]['config_bag_hash'][@options['sub_env']]['app_revisions'][@config['getter'].
|
109
|
+
@config[@options['env']]['config_bag_hash'][@options['sub_env']]['app_revisions'][@config['getter'].get_repository_from_role_name(@options['role'])] = "<use_default>"
|
114
110
|
|
115
111
|
end
|
116
112
|
|
@@ -0,0 +1,16 @@
|
|
1
|
+
|
2
|
+
class Cheftacular
|
3
|
+
class QueueMaster
|
4
|
+
def initialize options, config
|
5
|
+
@options, @config = options, config
|
6
|
+
end
|
7
|
+
|
8
|
+
def work_off_slack_queue
|
9
|
+
return true if @config['slack_queue'].empty?
|
10
|
+
|
11
|
+
@config['slack_queue'].each do |message|
|
12
|
+
@config['stateless_action'].slack(message)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -3,9 +3,20 @@ class Cheftacular
|
|
3
3
|
class StatelessActionDocumentation
|
4
4
|
def backups
|
5
5
|
@config['documentation']['stateless_action'] << [
|
6
|
-
"`cft
|
6
|
+
"`cft backups [activate|deactivate|load|run]` this command " +
|
7
7
|
"sets the fetch_backups and restore_backups flags in your config data bag for an environment. " +
|
8
|
-
"These can be used to give application developers a way to trigger / untrigger restores in an environment"
|
8
|
+
"These can be used to give application developers a way to trigger / untrigger restores in an environment",
|
9
|
+
|
10
|
+
[
|
11
|
+
" 1. `activate` will turn on automated backup running (turns on the flag for the env in the config bag).",
|
12
|
+
|
13
|
+
" 2. `deactivate` will turn off automated backup running.",
|
14
|
+
|
15
|
+
" 3. `load` will fetch the latest backup from the production primary **if it doesn't already exist on " +
|
16
|
+
"the server** and run the _backup loading command_ to load this backup into the env.",
|
17
|
+
|
18
|
+
" 4. `run` will simply just run the _backup loading command_ to load the latest backup onto the server."
|
19
|
+
]
|
9
20
|
]
|
10
21
|
|
11
22
|
@config['documentation']['application'] << @config['documentation']['stateless_action'].last
|
@@ -13,103 +24,203 @@ class Cheftacular
|
|
13
24
|
end
|
14
25
|
|
15
26
|
class StatelessAction
|
16
|
-
def backups
|
17
|
-
|
18
|
-
|
27
|
+
def backups command=''
|
28
|
+
command = ARGV[1] if command.blank?
|
29
|
+
|
30
|
+
raise "Unsupported command (#{ command }) for cft backups" unless command =~ /activate|deactivate|load|run/
|
31
|
+
|
32
|
+
self.send("backups_#{ command }")
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
19
36
|
|
20
|
-
|
37
|
+
def backups_activate restore_backup=true, fetch_backup=true
|
38
|
+
backups_toggle_setting(restore_backup, fetch_backup)
|
39
|
+
end
|
40
|
+
|
41
|
+
def backups_deactivate restore_backup=false, fetch_backup=false
|
42
|
+
backups_toggle_setting(restore_backup, fetch_backup)
|
43
|
+
end
|
44
|
+
|
45
|
+
def backups_load status_hash={}
|
46
|
+
old_role, old_env, backup_env = @options['role'], @options['env'], @config['cheftacular']['backup_config']['global_backup_environ']
|
21
47
|
|
22
|
-
|
23
|
-
|
24
|
-
|
48
|
+
if backup_env != @options['env']
|
49
|
+
@config['initializer'].initialize_data_bags_for_environment(backup_env, false, ['addresses', 'server_passwords'])
|
50
|
+
@config['initializer'].initialize_passwords backup_env
|
25
51
|
end
|
26
52
|
|
27
|
-
|
53
|
+
@options['role'] = @config['cheftacular']['backup_config']['global_backup_role_name']
|
54
|
+
@options['env'] = backup_env
|
28
55
|
|
29
|
-
|
30
|
-
|
56
|
+
puts "Deploying to backup master to force refresh of the ssh keys..."
|
57
|
+
#@config['action'].deploy
|
31
58
|
|
32
|
-
@
|
59
|
+
@options['role'] = old_role
|
60
|
+
@options['env'] = old_env
|
33
61
|
|
34
|
-
|
35
|
-
nodes = @config['getter'].get_true_node_objects true
|
62
|
+
target_db_primary = @config['getter'].get_db_primary_node
|
36
63
|
|
37
|
-
|
64
|
+
args_config = [
|
65
|
+
{ unless: "role[#{ @config['cheftacular']['backup_config']['global_backup_role_name'] }]" },
|
66
|
+
{ if: { not_env: backup_env } }
|
67
|
+
]
|
38
68
|
|
39
|
-
|
69
|
+
backup_master = @config['parser'].exclude_nodes( nodes, args_config, true)
|
70
|
+
backup_master_local_ip = @config['getter'].get_address_hash(backup_master.first.name)['priv']
|
40
71
|
|
41
|
-
|
42
|
-
backup_slave = @config['parser'].exclude_nodes( nodes, [{ unless: 'role[db_slave]' }, { if: { not_env: 'production' } }], true)
|
72
|
+
options, locs, ridley, logs_bag_hash, pass_bag_hash, bundle_command, cheftacular, passwords = @config['helper'].set_local_instance_vars
|
43
73
|
|
44
|
-
|
45
|
-
|
74
|
+
on ( backup_master.map { |n| @config['cheftacular']['deploy_user'] + "@" + n.public_ipaddress } ) do |host|
|
75
|
+
n = get_node_from_address(nodes, host.hostname)
|
46
76
|
|
47
|
-
|
77
|
+
puts("Beginning latest db_fetch_and_check for #{ n.name } (#{ n.public_ipaddress }) for env #{ options['env'] }") unless options['quiet']
|
48
78
|
|
49
|
-
|
50
|
-
|
79
|
+
status_hash['latest_backup'] = start_db_check_and_fetch( n.name, n.public_ipaddress, options, locs, cheftacular, passwords)
|
80
|
+
end
|
51
81
|
|
52
|
-
|
82
|
+
return false unless status_hash['latest_backup']['file_check']
|
53
83
|
|
54
|
-
|
55
|
-
|
84
|
+
on ( target_db_primary.map { |n| @config['cheftacular']['deploy_user'] + "@" + n.public_ipaddress } ) do |host|
|
85
|
+
n = get_node_from_address(nodes, host.hostname)
|
86
|
+
|
87
|
+
puts("Beginning db_backup_fetch for #{ n.name } (#{ n.public_ipaddress }) for env #{ options['env'] }") unless options['quiet']
|
88
|
+
|
89
|
+
start_db_backup_fetch( n.name, n.public_ipaddress, options, locs, cheftacular, passwords, backup_master_local_ip, status_hash['latest_backup'])
|
90
|
+
end
|
56
91
|
|
57
|
-
|
58
|
-
|
59
|
-
|
92
|
+
backups_run(nodes)
|
93
|
+
end
|
94
|
+
|
95
|
+
def backups_run
|
96
|
+
target_db_primary = @config['getter'].get_db_primary_node
|
97
|
+
applications_as_string = @config['getter'].get_repo_names_for_repositories.keys.join(',')
|
98
|
+
env_pg_pass = @config[@options['env']]['chef_passwords_bag_hash']['pg_pass']
|
99
|
+
|
100
|
+
options, locs, ridley, logs_bag_hash, pass_bag_hash, bundle_command, cheftacular, passwords = @config['helper'].set_local_instance_vars
|
101
|
+
|
102
|
+
on ( target_db_primary.map { |n| @config['cheftacular']['deploy_user'] + "@" + n.public_ipaddress } ) do |host|
|
103
|
+
n = get_node_from_address(nodes, host.hostname)
|
60
104
|
|
61
|
-
|
105
|
+
puts("Beginning db_backup_run for #{ n.name } (#{ n.public_ipaddress }) for env #{ options['env'] }") unless options['quiet']
|
62
106
|
|
63
|
-
|
107
|
+
start_db_backup_run( n.name, n.public_ipaddress, options, locs, cheftacular, passwords, applications_as_string, env_pg_pass )
|
64
108
|
end
|
65
109
|
end
|
110
|
+
|
111
|
+
def backups_toggle_setting restore_backup, fetch_backup
|
112
|
+
initial_fetch_backup = @config[@options['env']]['config_bag_hash'][@options['sub_env']]['fetch_backups']
|
113
|
+
initial_restore_backup = @config[@options['env']]['config_bag_hash'][@options['sub_env']]['restore_backups']
|
114
|
+
|
115
|
+
puts "For #{ @options['env'] } (sub-env: #{ @options['sub_env'] }) fetch backups was set to " +
|
116
|
+
"#{ initial_fetch_backup ? 'on' : 'off' } and restoring backups was set to #{ initial_restore_backup ? 'on' : 'off' }"
|
117
|
+
|
118
|
+
puts "For #{ @options['env'] } (sub-env: #{ @options['sub_env'] }) fetch backups is now set to " +
|
119
|
+
"#{ fetch_backup ? 'on' : 'off' } and restoring backups is now set to #{ restore_backup ? 'on' : 'off' }"
|
120
|
+
|
121
|
+
@config[@options['env']]['config_bag_hash'][@options['sub_env']]['fetch_backups'] = fetch_backup
|
122
|
+
@config[@options['env']]['config_bag_hash'][@options['sub_env']]['restore_backups'] = restore_backup
|
123
|
+
|
124
|
+
@config['ChefDataBag'].save_config_bag
|
125
|
+
|
126
|
+
puts "Triggering deploy on databases to refresh backup setting..."
|
127
|
+
|
128
|
+
@options['role'] = 'db_primary'
|
129
|
+
|
130
|
+
@config['action'].deploy
|
131
|
+
end
|
66
132
|
end
|
67
133
|
end
|
68
134
|
|
69
135
|
module SSHKit
|
70
136
|
module Backend
|
71
137
|
class Netssh
|
72
|
-
def
|
73
|
-
|
138
|
+
def start_db_check_and_fetch name, ip_address, options, locs, cheftacular, passwords, out=[], return_hash={ 'file_check' => false }
|
139
|
+
base_dir = cheftacular['backup_config']['global_backup_path']
|
74
140
|
|
75
|
-
|
141
|
+
if !sudo_test( passwords[ip_address], base_dir ) #true if dir exists
|
142
|
+
puts "#{ name } (#{ ip_address }) cannot run #{ __method__ } as there is no directory at #{ base_dir }!"
|
76
143
|
|
77
|
-
|
78
|
-
|
144
|
+
return return_hash
|
145
|
+
end
|
79
146
|
|
80
|
-
|
147
|
+
target_dir = case cheftacular['backup_filesystem']
|
148
|
+
when 'backup_gem'
|
149
|
+
backup_gem_dir_sort passwords[ip_address], cheftacular, base_dir
|
150
|
+
when 'raw'
|
151
|
+
File.join( base_dir, sudo_capture( passwords[ip_address], :ls, base_dir ).split(' ').last )
|
152
|
+
else
|
153
|
+
raise "#{ __method__ } does not currently support the #{ cheftacular['backup_filesystem'] } backup strategy at this time"
|
154
|
+
end
|
155
|
+
|
156
|
+
return_hash['file_check'] = true
|
157
|
+
return_hash['file_path'] = [target_dir].flatten.last
|
158
|
+
return_hash['file_dir'] = case cheftacular['backup_filesystem']
|
159
|
+
when 'backup_gem' then target_dir.first
|
160
|
+
when 'raw' then base_dir
|
161
|
+
end
|
162
|
+
|
163
|
+
return_hash
|
164
|
+
end
|
81
165
|
|
82
|
-
|
166
|
+
def start_db_backup_fetch name, ip_address, options, locs, cheftacular, passwords, backup_master_local_ip, backup_hash, out=[]
|
167
|
+
if sudo_test( passwords[ip_address], backup_path ) #true if dir exists
|
168
|
+
puts "#{ name } (#{ ip_address }) already has the backup at #{ backup_path }, skipping #{ __method__ }..."
|
83
169
|
|
84
|
-
|
85
|
-
|
86
|
-
"service postgresql restart"
|
87
|
-
]
|
170
|
+
return true
|
171
|
+
end
|
88
172
|
|
89
|
-
|
90
|
-
out << sudo_capture( passwords[ip_address], command )
|
173
|
+
sudo_execute( passwords[ip_address], :mkdir, '-p', backup_hash['file_dir'] )
|
91
174
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
end
|
175
|
+
sudo_execute( passwords[ip_address], :chown, "#{ cheftacular['deploy_user'] }:#{ cheftacular['deploy_user'] }", backup_hash['file_dir'] )
|
176
|
+
|
177
|
+
sudo_execute( passwords[ip_address], :chmod, cheftacular['backup_config']['backup_dir_mode'], backup_hash['file_dir'] )
|
96
178
|
|
97
|
-
#
|
179
|
+
execute( :scp, "#{ cheftacular['deploy_user'] }@#{ backup_master_local_ip }:#{ backup_hash['file_path'] }", backup_hash['file_dir'] )
|
98
180
|
|
99
|
-
|
181
|
+
puts "Finished transferring #{ backup_hash['file_path'] } to #{ name }(#{ ip_address })..."
|
182
|
+
end
|
100
183
|
|
101
|
-
|
184
|
+
def start_db_backup_run name, ip_address, options, locs, cheftacular, passwords, applications_as_string, env_pg_pass
|
185
|
+
puts "Beginning backup run on #{ name } (#{ ip_address }), this command may take a while to complete..."
|
186
|
+
case cheftacular['backup_filesystem']
|
187
|
+
when 'backup_gem'
|
188
|
+
#'ruby /root/backup_management.rb /mnt/postgresbackups/backups ENVIRONMENT APPLICATIONS PG_PASS > /root/restore.log 2>&1'
|
189
|
+
command = cheftacular['backup_config']['backup_load_command']
|
190
|
+
command = command.gsub('ENVIRONMENT', options['env']).gsub('APPLICATIONS', applications_as_string).gsub('PG_PASS', env_pg_pass)
|
102
191
|
|
103
|
-
|
192
|
+
sudo_execute( passwords[ip_address], command )
|
193
|
+
when 'raw'
|
194
|
+
end
|
195
|
+
|
196
|
+
puts "Finished executing backup command on #{ name } (#{ ip_address })"
|
104
197
|
end
|
105
198
|
|
106
|
-
def
|
107
|
-
|
108
|
-
|
109
|
-
|
199
|
+
def backup_gem_dir_sort password, cheftacular, base_dir
|
200
|
+
timestamp_dirs, check_dirs, target_dir = [], [], ''
|
201
|
+
|
202
|
+
dirs = sudo_capture( password, :ls, base_dir )
|
203
|
+
|
204
|
+
dirs.split(' ').each do |timestamp_dir|
|
205
|
+
next if timestamp_dir == '.' || timestamp_dir == '..'
|
206
|
+
|
207
|
+
timestamp_dirs << timestamp_dir
|
208
|
+
end
|
209
|
+
|
210
|
+
timestamp_dirs.each do |dir|
|
211
|
+
if check_dirs.empty?
|
212
|
+
check_dirs << dir
|
213
|
+
target_dir = dir
|
214
|
+
else
|
215
|
+
check_dirs.each do |cdir|
|
216
|
+
target_dir = dir if Date.parse(dir) >= Date.parse(target_dir)
|
217
|
+
end
|
218
|
+
end
|
110
219
|
end
|
111
220
|
|
112
|
-
|
221
|
+
target_file = sudo_capture( password, :ls, File.join(base_dir, target_dir) ).split(' ').last
|
222
|
+
|
223
|
+
[ File.join(base_dir, target_dir), File.join( target_dir, target_file )]
|
113
224
|
end
|
114
225
|
end
|
115
226
|
end
|
@@ -13,6 +13,15 @@ class Cheftacular
|
|
13
13
|
def check_cheftacular_yml_keys out=[], exit_on_missing=false, warn_on_missing=false
|
14
14
|
base_message = "Your cheftacular.yml is missing the key KEY, its default value is being set to DEFAULT for this run."
|
15
15
|
|
16
|
+
#############################2.7.0################################################
|
17
|
+
|
18
|
+
unless @config['cheftacular'].has_key?('backup_config')
|
19
|
+
#backup_config:global_backup_role_name
|
20
|
+
base_message.gsub('KEY', 'backup_config').gsub('DEFAULT', 'nil')
|
21
|
+
|
22
|
+
warn_on_missing = true
|
23
|
+
end
|
24
|
+
|
16
25
|
#############################2.6.0################################################
|
17
26
|
unless @config['cheftacular'].has_key?('route_dns_changes_via')
|
18
27
|
puts base_message.gsub('KEY', 'route_dns_changes_via').gsub('DEFAULT', @options['preferred_cloud'])
|