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.
Files changed (40) hide show
  1. checksums.yaml +5 -5
  2. data/lib/chef_backup.rb +8 -8
  3. data/lib/chef_backup/config.rb +17 -17
  4. data/lib/chef_backup/data_map.rb +18 -12
  5. data/lib/chef_backup/deep_merge.rb +145 -0
  6. data/lib/chef_backup/helpers.rb +46 -10
  7. data/lib/chef_backup/logger.rb +2 -2
  8. data/lib/chef_backup/mash.rb +226 -0
  9. data/lib/chef_backup/runner.rb +11 -13
  10. data/lib/chef_backup/strategy.rb +10 -10
  11. data/lib/chef_backup/strategy/backup/custom.rb +1 -2
  12. data/lib/chef_backup/strategy/backup/ebs.rb +3 -6
  13. data/lib/chef_backup/strategy/backup/lvm.rb +2 -4
  14. data/lib/chef_backup/strategy/backup/object.rb +2 -4
  15. data/lib/chef_backup/strategy/backup/tar.rb +23 -7
  16. data/lib/chef_backup/strategy/restore/tar.rb +69 -43
  17. data/lib/chef_backup/version.rb +1 -1
  18. metadata +20 -168
  19. data/.gitignore +0 -23
  20. data/.kitchen.yml +0 -30
  21. data/.rubocop.yml +0 -21
  22. data/.travis.yml +0 -6
  23. data/Gemfile +0 -4
  24. data/Guardfile +0 -22
  25. data/README.md +0 -21
  26. data/Rakefile +0 -44
  27. data/chef_backup.gemspec +0 -33
  28. data/spec/fixtures/chef-server-running.json +0 -589
  29. data/spec/spec_helper.rb +0 -98
  30. data/spec/unit/data_map_spec.rb +0 -59
  31. data/spec/unit/helpers_spec.rb +0 -88
  32. data/spec/unit/runner_spec.rb +0 -185
  33. data/spec/unit/shared_examples/helpers.rb +0 -20
  34. data/spec/unit/strategy/backup/lvm_spec.rb +0 -0
  35. data/spec/unit/strategy/backup/shared_examples/backup.rb +0 -92
  36. data/spec/unit/strategy/backup/tar_spec.rb +0 -327
  37. data/spec/unit/strategy/restore/lvm_spec.rb +0 -0
  38. data/spec/unit/strategy/restore/shared_examples/restore.rb +0 -84
  39. data/spec/unit/strategy/restore/tar_spec.rb +0 -255
  40. data/spec/unit/strategy_spec.rb +0 -36
@@ -1,5 +1,5 @@
1
- require 'fileutils'
2
- require 'pathname'
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['backup']['logfile'] || nil)
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['backup']['strategy']
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['restore_param']
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['strategy']
66
+ manifest["strategy"]
69
67
  elsif ebs_snapshot?
70
- 'ebs'
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 == '.tgz'
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('').to_s
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['restore_dir'] ||= begin
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, 'No manifest found in tarball')
136
+ ensure_file!(file, InvalidTarball, "No manifest found in tarball")
139
137
  JSON.parse(File.read(file))
140
138
  end
141
139
  end
@@ -2,16 +2,16 @@
2
2
  #
3
3
  # All Rights Reserved
4
4
 
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'
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
@@ -2,6 +2,5 @@
2
2
  # - Verify that backup executable exists and is runnable
3
3
  # - exec script
4
4
  class ChefBackup::Strategy::CustomBackup < ChefBackup::Strategy::TarBackup
5
- def backup
6
- end
5
+ def backup; end
7
6
  end
@@ -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
@@ -34,9 +34,7 @@ class ChefBackup::Strategy::LvmBackup < ChefBackup::Strategy::TarBackup
34
34
  # warn if vg space is low
35
35
  end
36
36
 
37
- def take_lvm_snapshot
38
- end
37
+ def take_lvm_snapshot; end
39
38
 
40
- def mount_lvm_snapshot
41
- end
39
+ def mount_lvm_snapshot; end
42
40
  end
@@ -21,9 +21,7 @@ class ChefBackup::Strategy::ObjectBackup < ChefBackup::Strategy::TarBackup
21
21
  cleanup
22
22
  end
23
23
 
24
- def verify_object
25
- end
24
+ def verify_object; end
26
25
 
27
- def knife_ec_backup
28
- end
26
+ def knife_ec_backup; end
29
27
  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(rabbitmq
123
+ DEFAULT_STATEFUL_SERVICES = %w[rabbitmq
112
124
  opscode-solr4
113
125
  elasticsearch
114
126
  redis_lb
115
127
  postgresql
116
- bookshelf).freeze
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: [:keepalived, :postgresql])
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 'fileutils'
2
- require 'pathname'
3
- require 'forwardable'
4
- require 'chef/mixin/deep_merge'
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 Chef::Mixin::DeepMerge
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['backup']['logfile'] || nil)
24
+ @log = ChefBackup::Logger.logger(service_config["backup"]["logfile"] || nil)
25
25
  end
26
26
 
27
27
  def restore
28
- log 'Restoring Chef Server from backup'
29
- cleanse_chef_server(config['agree_to_cleanse'])
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 'Performing HA restore - please ensure that keepalived is not running on the standby host'
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 'Restoration Completed!'
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['restore_dir'],
53
- 'manifest.json'))
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['services']['postgresql']['pg_dump_success'] && !frontend?
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('postgresql')
67
- sql_file = File.join(ChefBackup::Config['restore_dir'],
68
- "chef_backup-#{manifest['backup_time']}.sql")
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['services']['postgresql']['username']}",
74
+ "-u #{manifest["services"]["postgresql"]["username"]}",
73
75
  pgsql,
74
- "-U #{manifest['services']['postgresql']['username']}",
76
+ "-U #{manifest["services"]["postgresql"]["username"]}",
75
77
  "-d #{database_name}",
76
- "< #{sql_file}"
77
- ].join(' ')
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?('services') && manifest['services'].keys.each do |service|
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?('configs') && manifest['configs'].keys.each do |config|
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 = '/var/opt/opscode'
96
- sentinel = File.join(dir, 'bootstrapped')
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, 'w') { |file| file.write 'bootstrapped!' }
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['restore_dir'],
103
- manifest[type.to_s][name]['data_dir']))
104
- destination = manifest[type.to_s][name]['data_dir']
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('').to_s
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 'Fixing HA plugins directory (https://github.com/chef/chef-server/issues/115)'
117
- plugins_dir = '/var/opt/opscode/plugins'
118
- drbd_plugin = File.join(plugins_dir, 'chef-ha-drbd.rb')
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('/opt/opscode/chef-server-plugin.rb', drbd_plugin) unless
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 'Checking that the HA storage volume is mounted'
127
- ha_data_dir = manifest['ha']['path']
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('/proc/mounts').split("\n").grep(/#{ha_data_dir}/).count > 0
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 'Touching drbd_ready file'
140
- FileUtils.touch('/var/opt/opscode/drbd/drbd_ready') unless
141
- File.exist?('/var/opt/opscode/drbd/drbd_ready')
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 'Reconfiguring the Chef Server'
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 'Cleaning up any old files'
151
- shell_out!("#{ctl_command} cleanse #{agree || ''}")
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