chef_backup 0.0.1 → 0.1.0
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 +17 -17
- data/lib/chef_backup/data_map.rb +18 -12
- data/lib/chef_backup/deep_merge.rb +145 -0
- data/lib/chef_backup/helpers.rb +46 -10
- data/lib/chef_backup/logger.rb +2 -2
- data/lib/chef_backup/mash.rb +226 -0
- data/lib/chef_backup/runner.rb +11 -13
- 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 +23 -7
- data/lib/chef_backup/strategy/restore/tar.rb +69 -43
- data/lib/chef_backup/version.rb +1 -1
- metadata +20 -168
- data/.gitignore +0 -23
- data/.kitchen.yml +0 -30
- data/.rubocop.yml +0 -21
- 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 -589
- data/spec/spec_helper.rb +0 -98
- 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 -92
- data/spec/unit/strategy/backup/tar_spec.rb +0 -327
- 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 -255
- data/spec/unit/strategy_spec.rb +0 -36
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(service_config[
|
18
|
+
ChefBackup::Logger.logger(service_config["backup"]["logfile"] || nil)
|
21
19
|
end
|
22
20
|
|
23
21
|
#
|
@@ -47,7 +45,7 @@ module ChefBackup
|
|
47
45
|
# @return [String] String name of the configured backup strategy
|
48
46
|
#
|
49
47
|
def backup_strategy
|
50
|
-
service_config[
|
48
|
+
service_config["backup"]["strategy"]
|
51
49
|
end
|
52
50
|
|
53
51
|
#
|
@@ -55,7 +53,7 @@ module ChefBackup
|
|
55
53
|
# or a path to a tarball
|
56
54
|
#
|
57
55
|
def restore_param
|
58
|
-
config[
|
56
|
+
config["restore_param"]
|
59
57
|
end
|
60
58
|
|
61
59
|
#
|
@@ -65,9 +63,9 @@ module ChefBackup
|
|
65
63
|
@restore_strategy ||= begin
|
66
64
|
if tarball?
|
67
65
|
unpack_tarball
|
68
|
-
manifest[
|
66
|
+
manifest["strategy"]
|
69
67
|
elsif ebs_snapshot?
|
70
|
-
|
68
|
+
"ebs"
|
71
69
|
else
|
72
70
|
raise InvalidStrategy, "#{restore_param} is not a valid backup"
|
73
71
|
end
|
@@ -79,7 +77,7 @@ module ChefBackup
|
|
79
77
|
#
|
80
78
|
def tarball?
|
81
79
|
file = Pathname.new(File.expand_path(restore_param))
|
82
|
-
file.exist? && file.extname ==
|
80
|
+
file.exist? && file.extname == ".tgz"
|
83
81
|
end
|
84
82
|
|
85
83
|
#
|
@@ -104,7 +102,7 @@ module ChefBackup
|
|
104
102
|
#
|
105
103
|
def backup_name
|
106
104
|
if tarball?
|
107
|
-
Pathname.new(restore_param).basename.sub_ext(
|
105
|
+
Pathname.new(restore_param).basename.sub_ext("").to_s
|
108
106
|
elsif ebs_snapshot?
|
109
107
|
restore_param
|
110
108
|
end
|
@@ -117,7 +115,7 @@ module ChefBackup
|
|
117
115
|
# @return [String] A path to the restore directory
|
118
116
|
#
|
119
117
|
def restore_directory
|
120
|
-
config[
|
118
|
+
config["restore_dir"] ||= begin
|
121
119
|
dir_name = File.join(tmp_dir, backup_name)
|
122
120
|
if File.directory?(dir_name)
|
123
121
|
# clean restore directory if it exists
|
@@ -135,7 +133,7 @@ module ChefBackup
|
|
135
133
|
def manifest
|
136
134
|
@manifest ||= begin
|
137
135
|
file = "#{restore_directory}/manifest.json"
|
138
|
-
ensure_file!(file, InvalidTarball,
|
136
|
+
ensure_file!(file, InvalidTarball, "No manifest found in tarball")
|
139
137
|
JSON.parse(File.read(file))
|
140
138
|
end
|
141
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
|
@@ -56,8 +56,7 @@ class TarBackup
|
|
56
56
|
cmd = [chpst,
|
57
57
|
"-u #{pg_user}",
|
58
58
|
pg_dumpall,
|
59
|
-
"> #{sql_file}"
|
60
|
-
].join(' ')
|
59
|
+
"> #{sql_file}"].join(' ')
|
61
60
|
log "Dumping Postgresql database to #{sql_file}"
|
62
61
|
shell_out!(cmd, env: ["PGOPTIONS=#{pg_options}"])
|
63
62
|
data_map.services['postgresql']['pg_dump_success'] = true
|
@@ -85,11 +84,24 @@ class TarBackup
|
|
85
84
|
data_map.add_config(config, "/etc/#{config}")
|
86
85
|
end
|
87
86
|
|
87
|
+
populate_versions
|
88
|
+
|
88
89
|
# Don't forget the upgrades!
|
89
90
|
if service_config.key?('upgrades')
|
90
91
|
data_map.add_service('upgrades', service_config['upgrades']['dir'])
|
91
92
|
end
|
92
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
|
93
105
|
if ha? && !config_only?
|
94
106
|
data_map.add_service('keepalived', service_config['keepalived']['dir'])
|
95
107
|
data_map.add_ha_info('provider', service_config['ha']['provider'])
|
@@ -108,12 +120,12 @@ class TarBackup
|
|
108
120
|
end
|
109
121
|
end
|
110
122
|
|
111
|
-
DEFAULT_STATEFUL_SERVICES = %w
|
123
|
+
DEFAULT_STATEFUL_SERVICES = %w[rabbitmq
|
112
124
|
opscode-solr4
|
113
125
|
elasticsearch
|
114
126
|
redis_lb
|
115
127
|
postgresql
|
116
|
-
bookshelf
|
128
|
+
bookshelf].freeze
|
117
129
|
|
118
130
|
def stateful_services
|
119
131
|
if service_config.key?('drbd') && service_config['drbd']['enable'] == true
|
@@ -129,6 +141,10 @@ class TarBackup
|
|
129
141
|
[project_name] + enabled_addons.keys
|
130
142
|
end
|
131
143
|
|
144
|
+
def project_names
|
145
|
+
([project_name] + enabled_addons.keys).uniq
|
146
|
+
end
|
147
|
+
|
132
148
|
# The data_map is a working record of all of the data that is backed up.
|
133
149
|
def data_map
|
134
150
|
@data_map ||= ChefBackup::DataMap.new do |data|
|
@@ -150,7 +166,7 @@ class TarBackup
|
|
150
166
|
if backend? && !config_only?
|
151
167
|
if !online?
|
152
168
|
ask_to_go_offline unless offline_permission_granted?
|
153
|
-
stop_chef_server(except: [
|
169
|
+
stop_chef_server(except: %i[keepalived postgresql])
|
154
170
|
dump_db
|
155
171
|
stop_service(:postgresql)
|
156
172
|
stopped = true
|
@@ -181,7 +197,7 @@ class TarBackup
|
|
181
197
|
Dir["#{tmp_dir}/*"].map { |f| File.basename(f) }.join(' ')
|
182
198
|
].join(' ').strip
|
183
199
|
|
184
|
-
res = shell_out(cmd, cwd: tmp_dir)
|
200
|
+
res = shell_out!(cmd, cwd: tmp_dir)
|
185
201
|
res
|
186
202
|
end
|
187
203
|
|
@@ -189,7 +205,7 @@ class TarBackup
|
|
189
205
|
log "Exporting tarball to #{export_dir}"
|
190
206
|
cmd = "rsync -chaz #{tmp_dir}/#{export_filename} #{export_dir}/"
|
191
207
|
|
192
|
-
res = shell_out(cmd)
|
208
|
+
res = shell_out!(cmd)
|
193
209
|
res
|
194
210
|
end
|
195
211
|
|
@@ -1,7 +1,7 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
1
|
+
require "fileutils"
|
2
|
+
require "pathname"
|
3
|
+
require "forwardable"
|
4
|
+
require "chef_backup/deep_merge"
|
5
5
|
|
6
6
|
# rubocop:disable IndentationWidth
|
7
7
|
module ChefBackup
|
@@ -11,7 +11,7 @@ class TarRestore
|
|
11
11
|
# rubocop:enable IndentationWidth
|
12
12
|
include ChefBackup::Helpers
|
13
13
|
include ChefBackup::Exceptions
|
14
|
-
include
|
14
|
+
include ChefBackup::Mixin::DeepMerge
|
15
15
|
extend Forwardable
|
16
16
|
|
17
17
|
attr_accessor :tarball_path
|
@@ -21,14 +21,16 @@ class TarRestore
|
|
21
21
|
|
22
22
|
def initialize(path)
|
23
23
|
@tarball_path = path
|
24
|
-
@log = ChefBackup::Logger.logger(service_config[
|
24
|
+
@log = ChefBackup::Logger.logger(service_config["backup"]["logfile"] || nil)
|
25
25
|
end
|
26
26
|
|
27
27
|
def restore
|
28
|
-
log
|
29
|
-
|
28
|
+
log "Restoring Chef Server from backup"
|
29
|
+
return unless check_manifest_version
|
30
|
+
|
31
|
+
cleanse_chef_server(config["agree_to_cleanse"])
|
30
32
|
if ha?
|
31
|
-
log
|
33
|
+
log "Performing HA restore - please ensure that keepalived is not running on the standby host"
|
32
34
|
fix_ha_plugins
|
33
35
|
check_ha_volume
|
34
36
|
touch_drbd_ready
|
@@ -44,64 +46,63 @@ class TarRestore
|
|
44
46
|
reconfigure_add_ons
|
45
47
|
restart_add_ons
|
46
48
|
cleanup
|
47
|
-
log
|
49
|
+
log "Restoration Completed!"
|
48
50
|
end
|
49
51
|
|
50
52
|
def manifest
|
51
53
|
@manifest ||= begin
|
52
|
-
manifest = File.expand_path(File.join(ChefBackup::Config[
|
53
|
-
|
54
|
+
manifest = File.expand_path(File.join(ChefBackup::Config["restore_dir"],
|
55
|
+
"manifest.json"))
|
54
56
|
ensure_file!(manifest, InvalidManifest, "#{manifest} not found")
|
55
57
|
JSON.parse(File.read(manifest))
|
56
58
|
end
|
57
59
|
end
|
58
60
|
|
59
61
|
def restore_db_dump?
|
60
|
-
manifest[
|
62
|
+
manifest["services"]["postgresql"]["pg_dump_success"] && !frontend?
|
61
63
|
rescue NoMethodError
|
62
64
|
false
|
63
65
|
end
|
64
66
|
|
65
67
|
def import_db
|
66
|
-
start_service(
|
67
|
-
sql_file = File.join(ChefBackup::Config[
|
68
|
-
|
68
|
+
start_service("postgresql")
|
69
|
+
sql_file = File.join(ChefBackup::Config["restore_dir"],
|
70
|
+
"chef_backup-#{manifest["backup_time"]}.sql")
|
69
71
|
ensure_file!(sql_file, InvalidDatabaseDump, "#{sql_file} not found")
|
70
72
|
|
71
73
|
cmd = [chpst,
|
72
|
-
"-u #{manifest[
|
74
|
+
"-u #{manifest["services"]["postgresql"]["username"]}",
|
73
75
|
pgsql,
|
74
|
-
"-U #{manifest[
|
76
|
+
"-U #{manifest["services"]["postgresql"]["username"]}",
|
75
77
|
"-d #{database_name}",
|
76
|
-
"< #{sql_file}"
|
77
|
-
|
78
|
-
log 'Importing Database dump'
|
78
|
+
"< #{sql_file}"].join(" ")
|
79
|
+
log "Importing Database dump"
|
79
80
|
shell_out!(cmd, env: ["PGOPTIONS=#{pg_options}"])
|
80
81
|
end
|
81
82
|
|
82
83
|
def restore_services
|
83
|
-
manifest.key?(
|
84
|
+
manifest.key?("services") && manifest["services"].keys.each do |service|
|
84
85
|
restore_data(:services, service)
|
85
86
|
end
|
86
87
|
end
|
87
88
|
|
88
89
|
def restore_configs
|
89
|
-
manifest.key?(
|
90
|
+
manifest.key?("configs") && manifest["configs"].keys.each do |config|
|
90
91
|
restore_data(:configs, config)
|
91
92
|
end
|
92
93
|
end
|
93
94
|
|
94
95
|
def touch_sentinel
|
95
|
-
dir =
|
96
|
-
sentinel = File.join(dir,
|
96
|
+
dir = "/var/opt/opscode"
|
97
|
+
sentinel = File.join(dir, "bootstrapped")
|
97
98
|
FileUtils.mkdir_p(dir) unless File.directory?(dir)
|
98
|
-
File.open(sentinel,
|
99
|
+
File.open(sentinel, "w") { |file| file.write "bootstrapped!" }
|
99
100
|
end
|
100
101
|
|
101
102
|
def restore_data(type, name)
|
102
|
-
source = File.expand_path(File.join(config[
|
103
|
-
|
104
|
-
destination = manifest[type.to_s][name][
|
103
|
+
source = File.expand_path(File.join(config["restore_dir"],
|
104
|
+
manifest[type.to_s][name]["data_dir"]))
|
105
|
+
destination = manifest[type.to_s][name]["data_dir"]
|
105
106
|
FileUtils.mkdir_p(destination) unless File.directory?(destination)
|
106
107
|
cmd = "rsync -chaz --delete #{source}/ #{destination}"
|
107
108
|
log "Restoring the #{name} data"
|
@@ -109,22 +110,42 @@ class TarRestore
|
|
109
110
|
end
|
110
111
|
|
111
112
|
def backup_name
|
112
|
-
@backup_name ||= Pathname.new(tarball_path).basename.sub_ext(
|
113
|
+
@backup_name ||= Pathname.new(tarball_path).basename.sub_ext("").to_s
|
114
|
+
end
|
115
|
+
|
116
|
+
def check_manifest_version
|
117
|
+
unless manifest["versions"]
|
118
|
+
log "no version information in manifest"
|
119
|
+
return true
|
120
|
+
end
|
121
|
+
|
122
|
+
manifest["versions"].each_pair do |name, data|
|
123
|
+
installed = version_from_manifest_file(data["path"])
|
124
|
+
|
125
|
+
if installed == :no_version
|
126
|
+
log "Warning: #{name} @ #{data["version"]} not installed"
|
127
|
+
elsif installed["version"] != data["version"]
|
128
|
+
log "package #{name} #{installed["version"]} installed, but backup was"\
|
129
|
+
" from #{data["version"]}. Please install correct version, restore, then upgrade."
|
130
|
+
return false
|
131
|
+
end
|
132
|
+
end
|
133
|
+
true
|
113
134
|
end
|
114
135
|
|
115
136
|
def fix_ha_plugins
|
116
|
-
log
|
117
|
-
plugins_dir =
|
118
|
-
drbd_plugin = File.join(plugins_dir,
|
137
|
+
log "Fixing HA plugins directory (https://github.com/chef/chef-server/issues/115)"
|
138
|
+
plugins_dir = "/var/opt/opscode/plugins"
|
139
|
+
drbd_plugin = File.join(plugins_dir, "chef-ha-drbd.rb")
|
119
140
|
|
120
141
|
FileUtils.mkdir_p(plugins_dir) unless Dir.exist?(plugins_dir)
|
121
|
-
FileUtils.ln_sf(
|
142
|
+
FileUtils.ln_sf("/opt/opscode/chef-server-plugin.rb", drbd_plugin) unless
|
122
143
|
File.exist?(drbd_plugin)
|
123
144
|
end
|
124
145
|
|
125
146
|
def check_ha_volume
|
126
|
-
log
|
127
|
-
ha_data_dir = manifest[
|
147
|
+
log "Checking that the HA storage volume is mounted"
|
148
|
+
ha_data_dir = manifest["ha"]["path"]
|
128
149
|
|
129
150
|
unless ha_data_dir_mounted?(ha_data_dir)
|
130
151
|
raise "Please mount the data directory #{ha_data_dir} and perform any DRBD configuration before continuing"
|
@@ -132,23 +153,28 @@ class TarRestore
|
|
132
153
|
end
|
133
154
|
|
134
155
|
def ha_data_dir_mounted?(ha_data_dir)
|
135
|
-
File.read(
|
156
|
+
File.read("/proc/mounts").split("\n").grep(/#{ha_data_dir}/).count > 0
|
136
157
|
end
|
137
158
|
|
138
159
|
def touch_drbd_ready
|
139
|
-
log
|
140
|
-
FileUtils.touch(
|
141
|
-
File.exist?(
|
160
|
+
log "Touching drbd_ready file"
|
161
|
+
FileUtils.touch("/var/opt/opscode/drbd/drbd_ready") unless
|
162
|
+
File.exist?("/var/opt/opscode/drbd/drbd_ready")
|
142
163
|
end
|
143
164
|
|
144
165
|
def reconfigure_server
|
145
|
-
log
|
166
|
+
log "Reconfiguring the Chef Server"
|
146
167
|
shell_out("#{ctl_command} reconfigure")
|
147
168
|
end
|
148
169
|
|
149
170
|
def cleanse_chef_server(agree)
|
150
|
-
log
|
151
|
-
|
171
|
+
log "Cleaning up any old files"
|
172
|
+
# The chef-server-ctl cleanse command deliberately waits 60 seconds to give
|
173
|
+
# you an option to cancel it. Therefore, don't count it in the timeout that
|
174
|
+
# the user provided. If the user has eagerly dismissed that wait period,
|
175
|
+
# then don't elongate the timeout. The user can do this with the -c flag.
|
176
|
+
timeout = shell_timeout + (agree ? 0 : 60) if shell_timeout
|
177
|
+
shell_out!("#{ctl_command} cleanse #{agree || ""}", "timeout" => timeout)
|
152
178
|
end
|
153
179
|
|
154
180
|
def running_config
|