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.
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