chef_backup 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|