chef_backup 0.0.1.dev.2 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/lib/chef_backup.rb +8 -8
- data/lib/chef_backup/config.rb +22 -12
- data/lib/chef_backup/data_map.rb +23 -9
- data/lib/chef_backup/deep_merge.rb +145 -0
- data/lib/chef_backup/helpers.rb +169 -27
- data/lib/chef_backup/logger.rb +2 -2
- data/lib/chef_backup/mash.rb +226 -0
- data/lib/chef_backup/runner.rb +19 -14
- data/lib/chef_backup/strategy.rb +10 -10
- data/lib/chef_backup/strategy/backup/custom.rb +1 -2
- data/lib/chef_backup/strategy/backup/ebs.rb +3 -6
- data/lib/chef_backup/strategy/backup/lvm.rb +2 -4
- data/lib/chef_backup/strategy/backup/object.rb +2 -4
- data/lib/chef_backup/strategy/backup/tar.rb +116 -35
- data/lib/chef_backup/strategy/restore/tar.rb +103 -38
- data/lib/chef_backup/version.rb +1 -1
- metadata +22 -170
- data/.gitignore +0 -23
- data/.kitchen.yml +0 -30
- data/.rubocop.yml +0 -17
- data/.travis.yml +0 -6
- data/Gemfile +0 -4
- data/Guardfile +0 -22
- data/README.md +0 -21
- data/Rakefile +0 -44
- data/chef_backup.gemspec +0 -33
- data/spec/fixtures/chef-server-running.json +0 -584
- data/spec/spec_helper.rb +0 -103
- data/spec/unit/data_map_spec.rb +0 -59
- data/spec/unit/helpers_spec.rb +0 -88
- data/spec/unit/runner_spec.rb +0 -185
- data/spec/unit/shared_examples/helpers.rb +0 -20
- data/spec/unit/strategy/backup/lvm_spec.rb +0 -0
- data/spec/unit/strategy/backup/shared_examples/backup.rb +0 -74
- data/spec/unit/strategy/backup/tar_spec.rb +0 -294
- data/spec/unit/strategy/restore/lvm_spec.rb +0 -0
- data/spec/unit/strategy/restore/shared_examples/restore.rb +0 -84
- data/spec/unit/strategy/restore/tar_spec.rb +0 -238
- data/spec/unit/strategy_spec.rb +0 -36
data/lib/chef_backup/logger.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "highline"
|
2
2
|
|
3
3
|
module ChefBackup
|
4
4
|
# Basic Logging Class
|
@@ -15,7 +15,7 @@ module ChefBackup
|
|
15
15
|
attr_accessor :stdout
|
16
16
|
|
17
17
|
def initialize(logfile = nil)
|
18
|
-
$stdout = logfile ? File.open(logfile,
|
18
|
+
$stdout = logfile ? File.open(logfile, "ab") : $stdout
|
19
19
|
@highline = HighLine.new($stdin, $stdout)
|
20
20
|
end
|
21
21
|
|
@@ -0,0 +1,226 @@
|
|
1
|
+
# Copyright 2009-2016, Dan Kubb
|
2
|
+
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
# a copy of this software and associated documentation files (the
|
5
|
+
# "Software"), to deal in the Software without restriction, including
|
6
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
# the following conditions:
|
10
|
+
|
11
|
+
# The above copyright notice and this permission notice shall be
|
12
|
+
# included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
+
|
22
|
+
# ---
|
23
|
+
# ---
|
24
|
+
|
25
|
+
# Some portions of blank.rb and mash.rb are verbatim copies of software
|
26
|
+
# licensed under the MIT license. That license is included below:
|
27
|
+
|
28
|
+
# Copyright 2005-2016, David Heinemeier Hansson
|
29
|
+
|
30
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
31
|
+
# a copy of this software and associated documentation files (the
|
32
|
+
# "Software"), to deal in the Software without restriction, including
|
33
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
34
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
35
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
36
|
+
# the following conditions:
|
37
|
+
|
38
|
+
# The above copyright notice and this permission notice shall be
|
39
|
+
# included in all copies or substantial portions of the Software.
|
40
|
+
|
41
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
42
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
43
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
44
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
45
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
46
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
47
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
48
|
+
|
49
|
+
# This class has dubious semantics and we only have it so that people can write
|
50
|
+
# params[:key] instead of params['key'].
|
51
|
+
class Mash < Hash
|
52
|
+
|
53
|
+
# @param constructor<Object>
|
54
|
+
# The default value for the mash. Defaults to an empty hash.
|
55
|
+
#
|
56
|
+
# @details [Alternatives]
|
57
|
+
# If constructor is a Hash, a new mash will be created based on the keys of
|
58
|
+
# the hash and no default value will be set.
|
59
|
+
def initialize(constructor = {})
|
60
|
+
if constructor.is_a?(Hash)
|
61
|
+
super()
|
62
|
+
update(constructor)
|
63
|
+
else
|
64
|
+
super(constructor)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# @param orig<Object> Mash being copied
|
69
|
+
#
|
70
|
+
# @return [Object] A new copied Mash
|
71
|
+
def initialize_copy(orig)
|
72
|
+
super
|
73
|
+
# Handle nested values
|
74
|
+
each do |k, v|
|
75
|
+
if v.kind_of?(Mash) || v.is_a?(Array)
|
76
|
+
self[k] = v.dup
|
77
|
+
end
|
78
|
+
end
|
79
|
+
self
|
80
|
+
end
|
81
|
+
|
82
|
+
# @param key<Object> The default value for the mash. Defaults to nil.
|
83
|
+
#
|
84
|
+
# @details [Alternatives]
|
85
|
+
# If key is a Symbol and it is a key in the mash, then the default value will
|
86
|
+
# be set to the value matching the key.
|
87
|
+
def default(key = nil)
|
88
|
+
if key.is_a?(Symbol) && include?(key = key.to_s)
|
89
|
+
self[key]
|
90
|
+
else
|
91
|
+
super
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
|
96
|
+
alias_method :regular_update, :update unless method_defined?(:regular_update)
|
97
|
+
|
98
|
+
# @param key<Object> The key to set.
|
99
|
+
# @param value<Object>
|
100
|
+
# The value to set the key to.
|
101
|
+
#
|
102
|
+
# @see Mash#convert_key
|
103
|
+
# @see Mash#convert_value
|
104
|
+
def []=(key, value)
|
105
|
+
regular_writer(convert_key(key), convert_value(value))
|
106
|
+
end
|
107
|
+
|
108
|
+
# @param other_hash<Hash>
|
109
|
+
# A hash to update values in the mash with. The keys and the values will be
|
110
|
+
# converted to Mash format.
|
111
|
+
#
|
112
|
+
# @return [Mash] The updated mash.
|
113
|
+
def update(other_hash)
|
114
|
+
other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) }
|
115
|
+
self
|
116
|
+
end
|
117
|
+
|
118
|
+
alias_method :merge!, :update
|
119
|
+
|
120
|
+
# @param key<Object> The key to check for. This will be run through convert_key.
|
121
|
+
#
|
122
|
+
# @return [Boolean] True if the key exists in the mash.
|
123
|
+
def key?(key)
|
124
|
+
super(convert_key(key))
|
125
|
+
end
|
126
|
+
|
127
|
+
# def include? def has_key? def member?
|
128
|
+
alias_method :include?, :key?
|
129
|
+
alias_method :has_key?, :key?
|
130
|
+
alias_method :member?, :key?
|
131
|
+
|
132
|
+
# @param key<Object> The key to fetch. This will be run through convert_key.
|
133
|
+
# @param *extras<Array> Default value.
|
134
|
+
#
|
135
|
+
# @return [Object] The value at key or the default value.
|
136
|
+
def fetch(key, *extras)
|
137
|
+
super(convert_key(key), *extras)
|
138
|
+
end
|
139
|
+
|
140
|
+
# @param *indices<Array>
|
141
|
+
# The keys to retrieve values for. These will be run through +convert_key+.
|
142
|
+
#
|
143
|
+
# @return [Array] The values at each of the provided keys
|
144
|
+
def values_at(*indices)
|
145
|
+
indices.collect { |key| self[convert_key(key)] }
|
146
|
+
end
|
147
|
+
|
148
|
+
# @param hash<Hash> The hash to merge with the mash.
|
149
|
+
#
|
150
|
+
# @return [Mash] A new mash with the hash values merged in.
|
151
|
+
def merge(hash)
|
152
|
+
self.dup.update(hash)
|
153
|
+
end
|
154
|
+
|
155
|
+
# @param key<Object>
|
156
|
+
# The key to delete from the mash.\
|
157
|
+
def delete(key)
|
158
|
+
super(convert_key(key))
|
159
|
+
end
|
160
|
+
|
161
|
+
# @param *rejected<Array[(String, Symbol)] The mash keys to exclude.
|
162
|
+
#
|
163
|
+
# @return [Mash] A new mash without the selected keys.
|
164
|
+
#
|
165
|
+
# @example
|
166
|
+
# { :one => 1, :two => 2, :three => 3 }.except(:one)
|
167
|
+
# #=> { "two" => 2, "three" => 3 }
|
168
|
+
def except(*keys)
|
169
|
+
super(*keys.map { |k| convert_key(k) })
|
170
|
+
end
|
171
|
+
|
172
|
+
# Used to provide the same interface as Hash.
|
173
|
+
#
|
174
|
+
# @return [Mash] This mash unchanged.
|
175
|
+
def stringify_keys!; self end
|
176
|
+
|
177
|
+
# @return [Hash] The mash as a Hash with symbolized keys.
|
178
|
+
def symbolize_keys
|
179
|
+
h = Hash.new(default)
|
180
|
+
each { |key, val| h[key.to_sym] = val }
|
181
|
+
h
|
182
|
+
end
|
183
|
+
|
184
|
+
# @return [Hash] The mash as a Hash with string keys.
|
185
|
+
def to_hash
|
186
|
+
Hash.new(default).merge(self)
|
187
|
+
end
|
188
|
+
|
189
|
+
# @return [Mash] Convert a Hash into a Mash
|
190
|
+
# The input Hash's default value is maintained
|
191
|
+
def self.from_hash(hash)
|
192
|
+
mash = Mash.new(hash)
|
193
|
+
mash.default = hash.default
|
194
|
+
mash
|
195
|
+
end
|
196
|
+
|
197
|
+
protected
|
198
|
+
|
199
|
+
# @param key<Object> The key to convert.
|
200
|
+
#
|
201
|
+
# @param [Object]
|
202
|
+
# The converted key. If the key was a symbol, it will be converted to a
|
203
|
+
# string.
|
204
|
+
#
|
205
|
+
# @api private
|
206
|
+
def convert_key(key)
|
207
|
+
key.kind_of?(Symbol) ? key.to_s : key
|
208
|
+
end
|
209
|
+
|
210
|
+
# @param value<Object> The value to convert.
|
211
|
+
#
|
212
|
+
# @return [Object]
|
213
|
+
# The converted value. A Hash or an Array of hashes, will be converted to
|
214
|
+
# their Mash equivalents.
|
215
|
+
#
|
216
|
+
# @api private
|
217
|
+
def convert_value(value)
|
218
|
+
if value.class == Hash
|
219
|
+
Mash.from_hash(value)
|
220
|
+
elsif value.is_a?(Array)
|
221
|
+
value.collect { |e| convert_value(e) }
|
222
|
+
else
|
223
|
+
value
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
data/lib/chef_backup/runner.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "fileutils"
|
2
|
+
require "pathname"
|
3
3
|
|
4
4
|
module ChefBackup
|
5
5
|
# ChefBackup::Runner class initializes the strategy and runs the action
|
@@ -7,8 +7,6 @@ module ChefBackup
|
|
7
7
|
include ChefBackup::Helpers
|
8
8
|
include ChefBackup::Exceptions
|
9
9
|
|
10
|
-
attr_reader :restore_param
|
11
|
-
|
12
10
|
#
|
13
11
|
# @param running_config [Hash] A hash of the private-chef-running.json
|
14
12
|
# or the CLI args for a restore
|
@@ -17,7 +15,7 @@ module ChefBackup
|
|
17
15
|
#
|
18
16
|
def initialize(running_config)
|
19
17
|
ChefBackup::Config.config = running_config
|
20
|
-
ChefBackup::Logger.logger(
|
18
|
+
ChefBackup::Logger.logger(service_config["backup"]["logfile"] || nil)
|
21
19
|
end
|
22
20
|
|
23
21
|
#
|
@@ -28,6 +26,13 @@ module ChefBackup
|
|
28
26
|
@backup.backup
|
29
27
|
end
|
30
28
|
|
29
|
+
#
|
30
|
+
# @return [ChefBackup::Config]
|
31
|
+
#
|
32
|
+
def config
|
33
|
+
ChefBackup::Config.config
|
34
|
+
end
|
35
|
+
|
31
36
|
#
|
32
37
|
# @return [TrueClass, FalseClass] Execute Chef Server restore
|
33
38
|
#
|
@@ -40,7 +45,7 @@ module ChefBackup
|
|
40
45
|
# @return [String] String name of the configured backup strategy
|
41
46
|
#
|
42
47
|
def backup_strategy
|
43
|
-
|
48
|
+
service_config["backup"]["strategy"]
|
44
49
|
end
|
45
50
|
|
46
51
|
#
|
@@ -48,7 +53,7 @@ module ChefBackup
|
|
48
53
|
# or a path to a tarball
|
49
54
|
#
|
50
55
|
def restore_param
|
51
|
-
config[
|
56
|
+
config["restore_param"]
|
52
57
|
end
|
53
58
|
|
54
59
|
#
|
@@ -58,11 +63,11 @@ module ChefBackup
|
|
58
63
|
@restore_strategy ||= begin
|
59
64
|
if tarball?
|
60
65
|
unpack_tarball
|
61
|
-
manifest[
|
66
|
+
manifest["strategy"]
|
62
67
|
elsif ebs_snapshot?
|
63
|
-
|
68
|
+
"ebs"
|
64
69
|
else
|
65
|
-
|
70
|
+
raise InvalidStrategy, "#{restore_param} is not a valid backup"
|
66
71
|
end
|
67
72
|
end
|
68
73
|
end
|
@@ -72,7 +77,7 @@ module ChefBackup
|
|
72
77
|
#
|
73
78
|
def tarball?
|
74
79
|
file = Pathname.new(File.expand_path(restore_param))
|
75
|
-
file.exist? && file.extname ==
|
80
|
+
file.exist? && file.extname == ".tgz"
|
76
81
|
end
|
77
82
|
|
78
83
|
#
|
@@ -97,7 +102,7 @@ module ChefBackup
|
|
97
102
|
#
|
98
103
|
def backup_name
|
99
104
|
if tarball?
|
100
|
-
Pathname.new(restore_param).basename.sub_ext(
|
105
|
+
Pathname.new(restore_param).basename.sub_ext("").to_s
|
101
106
|
elsif ebs_snapshot?
|
102
107
|
restore_param
|
103
108
|
end
|
@@ -110,7 +115,7 @@ module ChefBackup
|
|
110
115
|
# @return [String] A path to the restore directory
|
111
116
|
#
|
112
117
|
def restore_directory
|
113
|
-
config[
|
118
|
+
config["restore_dir"] ||= begin
|
114
119
|
dir_name = File.join(tmp_dir, backup_name)
|
115
120
|
if File.directory?(dir_name)
|
116
121
|
# clean restore directory if it exists
|
@@ -128,7 +133,7 @@ module ChefBackup
|
|
128
133
|
def manifest
|
129
134
|
@manifest ||= begin
|
130
135
|
file = "#{restore_directory}/manifest.json"
|
131
|
-
ensure_file!(file, InvalidTarball,
|
136
|
+
ensure_file!(file, InvalidTarball, "No manifest found in tarball")
|
132
137
|
JSON.parse(File.read(file))
|
133
138
|
end
|
134
139
|
end
|
data/lib/chef_backup/strategy.rb
CHANGED
@@ -2,16 +2,16 @@
|
|
2
2
|
#
|
3
3
|
# All Rights Reserved
|
4
4
|
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require
|
11
|
-
require
|
12
|
-
require
|
13
|
-
require
|
14
|
-
require
|
5
|
+
require "chef_backup/strategy/backup/tar"
|
6
|
+
require "chef_backup/strategy/backup/lvm"
|
7
|
+
require "chef_backup/strategy/backup/ebs"
|
8
|
+
require "chef_backup/strategy/backup/object"
|
9
|
+
require "chef_backup/strategy/backup/custom"
|
10
|
+
require "chef_backup/strategy/restore/tar"
|
11
|
+
require "chef_backup/strategy/restore/lvm"
|
12
|
+
require "chef_backup/strategy/restore/ebs"
|
13
|
+
require "chef_backup/strategy/restore/object"
|
14
|
+
require "chef_backup/strategy/restore/custom"
|
15
15
|
|
16
16
|
module ChefBackup
|
17
17
|
# ChefBackup::Strategy factory returns an ChefBackup::Strategy object
|
@@ -17,12 +17,9 @@ class ChefBackup::Strategy::EbsBackup < ChefBackup::Strategy::TarBackup
|
|
17
17
|
cleanup
|
18
18
|
end
|
19
19
|
|
20
|
-
def verify_ebs
|
21
|
-
end
|
20
|
+
def verify_ebs; end
|
22
21
|
|
23
|
-
def take_ebs_snapshot
|
24
|
-
end
|
22
|
+
def take_ebs_snapshot; end
|
25
23
|
|
26
|
-
def copy_opscode_config
|
27
|
-
end
|
24
|
+
def copy_opscode_config; end
|
28
25
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'fileutils'
|
2
2
|
require 'json'
|
3
3
|
require 'time'
|
4
|
+
require 'highline/import'
|
4
5
|
|
5
6
|
# rubocop:disable IndentationWidth
|
6
7
|
module ChefBackup
|
@@ -26,8 +27,8 @@ class TarBackup
|
|
26
27
|
def export_dir
|
27
28
|
@export_dir ||= begin
|
28
29
|
dir =
|
29
|
-
if
|
30
|
-
|
30
|
+
if service_config['backup']['export_dir']
|
31
|
+
service_config['backup']['export_dir']
|
31
32
|
else
|
32
33
|
msg = ["backup['export_dir'] has not been set.",
|
33
34
|
'defaulting to: /var/opt/chef-backups'].join(' ')
|
@@ -46,33 +47,65 @@ class TarBackup
|
|
46
47
|
#
|
47
48
|
def dump_db
|
48
49
|
return true unless pg_dump?
|
49
|
-
|
50
|
+
if external_pg?
|
51
|
+
log('Cannot backup external postgresql', :warn)
|
52
|
+
return false
|
53
|
+
end
|
54
|
+
pg_user = service_config['postgresql']['username']
|
50
55
|
sql_file = "#{tmp_dir}/chef_backup-#{backup_time}.sql"
|
51
|
-
cmd = [
|
56
|
+
cmd = [chpst,
|
52
57
|
"-u #{pg_user}",
|
53
|
-
|
54
|
-
"> #{sql_file}"
|
55
|
-
].join(' ')
|
58
|
+
pg_dumpall,
|
59
|
+
"> #{sql_file}"].join(' ')
|
56
60
|
log "Dumping Postgresql database to #{sql_file}"
|
57
|
-
shell_out!(cmd)
|
61
|
+
shell_out!(cmd, env: ["PGOPTIONS=#{pg_options}"])
|
58
62
|
data_map.services['postgresql']['pg_dump_success'] = true
|
59
63
|
data_map.services['postgresql']['username'] = pg_user
|
60
64
|
true
|
61
65
|
end
|
62
66
|
|
67
|
+
def chpst
|
68
|
+
"#{base_install_dir}/embedded/bin/chpst"
|
69
|
+
end
|
70
|
+
|
71
|
+
def pg_dumpall
|
72
|
+
"#{base_install_dir}/embedded/bin/pg_dumpall"
|
73
|
+
end
|
74
|
+
|
63
75
|
def populate_data_map
|
64
|
-
|
65
|
-
|
66
|
-
|
76
|
+
unless config_only?
|
77
|
+
stateful_services.each do |service|
|
78
|
+
next unless service_config.key?(service)
|
79
|
+
data_map.add_service(service, service_config[service]['data_dir'])
|
80
|
+
end
|
67
81
|
end
|
68
82
|
|
69
83
|
config_directories.each do |config|
|
70
84
|
data_map.add_config(config, "/etc/#{config}")
|
71
85
|
end
|
72
86
|
|
87
|
+
populate_versions
|
88
|
+
|
73
89
|
# Don't forget the upgrades!
|
74
|
-
if
|
75
|
-
data_map.add_service('upgrades',
|
90
|
+
if service_config.key?('upgrades')
|
91
|
+
data_map.add_service('upgrades', service_config['upgrades']['dir'])
|
92
|
+
end
|
93
|
+
|
94
|
+
add_ha_services
|
95
|
+
end
|
96
|
+
|
97
|
+
def populate_versions
|
98
|
+
project_names.each do |project|
|
99
|
+
path = File.join(addon_install_dir(project), '/version-manifest.json')
|
100
|
+
data_map.add_version(project, version_from_manifest_file(path))
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def add_ha_services
|
105
|
+
if ha? && !config_only?
|
106
|
+
data_map.add_service('keepalived', service_config['keepalived']['dir'])
|
107
|
+
data_map.add_ha_info('provider', service_config['ha']['provider'])
|
108
|
+
data_map.add_ha_info('path', service_config['ha']['path'])
|
76
109
|
end
|
77
110
|
end
|
78
111
|
|
@@ -87,22 +120,29 @@ class TarBackup
|
|
87
120
|
end
|
88
121
|
end
|
89
122
|
|
123
|
+
DEFAULT_STATEFUL_SERVICES = %w[rabbitmq
|
124
|
+
opscode-solr4
|
125
|
+
elasticsearch
|
126
|
+
redis_lb
|
127
|
+
postgresql
|
128
|
+
bookshelf].freeze
|
129
|
+
|
90
130
|
def stateful_services
|
91
|
-
if
|
131
|
+
if service_config.key?('drbd') && service_config['drbd']['enable'] == true
|
92
132
|
['drbd']
|
93
133
|
else
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
redis_lb
|
98
|
-
postgresql
|
99
|
-
bookshelf
|
100
|
-
)
|
134
|
+
DEFAULT_STATEFUL_SERVICES.select do |service|
|
135
|
+
service_enabled?(service)
|
136
|
+
end
|
101
137
|
end
|
102
138
|
end
|
103
139
|
|
104
140
|
def config_directories
|
105
|
-
|
141
|
+
[project_name] + enabled_addons.keys
|
142
|
+
end
|
143
|
+
|
144
|
+
def project_names
|
145
|
+
([project_name] + enabled_addons.keys).uniq
|
106
146
|
end
|
107
147
|
|
108
148
|
# The data_map is a working record of all of the data that is backed up.
|
@@ -110,30 +150,38 @@ class TarBackup
|
|
110
150
|
@data_map ||= ChefBackup::DataMap.new do |data|
|
111
151
|
data.backup_time = backup_time
|
112
152
|
data.strategy = strategy
|
153
|
+
data.topology = topology
|
113
154
|
end
|
114
155
|
end
|
115
156
|
|
116
157
|
def not_implemented
|
117
158
|
msg = "#{caller[0].split[1]} is not implemented for this strategy"
|
118
|
-
|
159
|
+
raise NotImplementedError, msg
|
119
160
|
end
|
120
161
|
|
121
162
|
def backup
|
122
|
-
log
|
163
|
+
log "Starting Chef Server backup #{config_only? ? '(config only)' : ''}"
|
123
164
|
populate_data_map
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
165
|
+
stopped = false
|
166
|
+
if backend? && !config_only?
|
167
|
+
if !online?
|
168
|
+
ask_to_go_offline unless offline_permission_granted?
|
169
|
+
stop_chef_server(except: %i[keepalived postgresql])
|
170
|
+
dump_db
|
171
|
+
stop_service(:postgresql)
|
172
|
+
stopped = true
|
173
|
+
else
|
174
|
+
dump_db
|
175
|
+
end
|
128
176
|
end
|
129
177
|
write_manifest
|
130
178
|
create_tarball
|
131
|
-
start_chef_server if
|
179
|
+
start_chef_server if stopped
|
132
180
|
export_tarball
|
133
181
|
cleanup
|
134
182
|
log 'Backup Complete!'
|
135
183
|
rescue => e
|
136
|
-
log "Something
|
184
|
+
log "Something went terribly wrong, aborting backup", :error
|
137
185
|
log e.message, :error
|
138
186
|
cleanup
|
139
187
|
start_chef_server
|
@@ -143,27 +191,60 @@ class TarBackup
|
|
143
191
|
def create_tarball
|
144
192
|
log 'Creating backup tarball'
|
145
193
|
cmd = [
|
146
|
-
"tar -czf #{tmp_dir}
|
194
|
+
"tar -czf #{tmp_dir}/#{export_filename}",
|
147
195
|
data_map.services.map { |_, v| v['data_dir'] }.compact.join(' '),
|
148
196
|
data_map.configs.map { |_, v| v['data_dir'] }.compact.join(' '),
|
149
197
|
Dir["#{tmp_dir}/*"].map { |f| File.basename(f) }.join(' ')
|
150
198
|
].join(' ').strip
|
151
199
|
|
152
|
-
res = shell_out(cmd, cwd: tmp_dir)
|
200
|
+
res = shell_out!(cmd, cwd: tmp_dir)
|
153
201
|
res
|
154
202
|
end
|
155
203
|
|
156
204
|
def export_tarball
|
157
205
|
log "Exporting tarball to #{export_dir}"
|
158
|
-
cmd = "rsync -chaz #{tmp_dir}
|
206
|
+
cmd = "rsync -chaz #{tmp_dir}/#{export_filename} #{export_dir}/"
|
159
207
|
|
160
|
-
res = shell_out(cmd)
|
208
|
+
res = shell_out!(cmd)
|
161
209
|
res
|
162
210
|
end
|
163
211
|
|
212
|
+
def export_filename
|
213
|
+
postfix = if config_only?
|
214
|
+
'-config'
|
215
|
+
else
|
216
|
+
''
|
217
|
+
end
|
218
|
+
"chef-backup#{postfix}-#{backup_time}.tgz"
|
219
|
+
end
|
220
|
+
|
221
|
+
def service_enabled?(service)
|
222
|
+
service_config[service] && service_config[service]['enable'] && !service_config[service]['external']
|
223
|
+
end
|
224
|
+
|
225
|
+
def external_pg?
|
226
|
+
service_config['postgresql']['external']
|
227
|
+
end
|
228
|
+
|
164
229
|
def pg_dump?
|
165
230
|
# defaults to true
|
166
|
-
|
231
|
+
service_config['backup']['always_dump_db']
|
232
|
+
end
|
233
|
+
|
234
|
+
def offline_permission_granted?
|
235
|
+
service_config['backup']['agree_to_go_offline']
|
236
|
+
end
|
237
|
+
|
238
|
+
def config_only?
|
239
|
+
service_config['backup']['config_only']
|
240
|
+
end
|
241
|
+
|
242
|
+
def ask_to_go_offline
|
243
|
+
msg = 'WARNING: Offline backup mode must stop your Chef server before '
|
244
|
+
msg << 'continuing. You can skip this message by passing a "--yes" '
|
245
|
+
msg << 'argument. Do you wish to proceed? (y/N):'
|
246
|
+
|
247
|
+
exit(1) unless ask(msg) =~ /^y/i
|
167
248
|
end
|
168
249
|
end
|
169
250
|
end
|