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.
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 +22 -12
  4. data/lib/chef_backup/data_map.rb +23 -9
  5. data/lib/chef_backup/deep_merge.rb +145 -0
  6. data/lib/chef_backup/helpers.rb +169 -27
  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 +19 -14
  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 +116 -35
  16. data/lib/chef_backup/strategy/restore/tar.rb +103 -38
  17. data/lib/chef_backup/version.rb +1 -1
  18. metadata +22 -170
  19. data/.gitignore +0 -23
  20. data/.kitchen.yml +0 -30
  21. data/.rubocop.yml +0 -17
  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 -584
  29. data/spec/spec_helper.rb +0 -103
  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 -74
  36. data/spec/unit/strategy/backup/tar_spec.rb +0 -294
  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 -238
  40. data/spec/unit/strategy_spec.rb +0 -36
@@ -1,4 +1,4 @@
1
- require 'highline'
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, 'ab') : $stdout
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
@@ -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(private_chef['backup']['logfile'] || nil)
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
- config['private_chef']['backup']['strategy']
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['restore_param']
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['strategy']
66
+ manifest["strategy"]
62
67
  elsif ebs_snapshot?
63
- 'ebs'
68
+ "ebs"
64
69
  else
65
- fail InvalidStrategy, "#{restore_param} is not a valid backup"
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 == '.tgz'
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('').to_s
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['restore_dir'] ||= begin
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, 'No manifest found in tarball')
136
+ ensure_file!(file, InvalidTarball, "No manifest found in tarball")
132
137
  JSON.parse(File.read(file))
133
138
  end
134
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
@@ -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 private_chef['backup']['export_dir']
30
- private_chef['backup']['export_dir']
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
- pg_user = private_chef['postgresql']['username']
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 = ['/opt/opscode/embedded/bin/chpst',
56
+ cmd = [chpst,
52
57
  "-u #{pg_user}",
53
- '/opt/opscode/embedded/bin/pg_dumpall',
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
- stateful_services.each do |service|
65
- next unless private_chef.key?(service)
66
- data_map.add_service(service, private_chef[service]['data_dir'])
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 private_chef.key?('upgrades')
75
- data_map.add_service('upgrades', private_chef['upgrades']['dir'])
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 private_chef.key?('drbd') && private_chef['drbd']['enable'] == true
131
+ if service_config.key?('drbd') && service_config['drbd']['enable'] == true
92
132
  ['drbd']
93
133
  else
94
- %w(
95
- rabbitmq
96
- opscode-solr4
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
- %w(opscode) + enabled_addons
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
- fail NotImplementedError, msg
159
+ raise NotImplementedError, msg
119
160
  end
120
161
 
121
162
  def backup
122
- log 'Starting Chef Server backup'
163
+ log "Starting Chef Server backup #{config_only? ? '(config only)' : ''}"
123
164
  populate_data_map
124
- if backend?
125
- stop_chef_server(except: [:keepalived, :postgresql]) unless online?
126
- dump_db
127
- stop_service(:postgresql) unless online?
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 backend? && !online?
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 wen't terribly wrong, aborting backup", :error
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}/chef-backup-#{backup_time}.tgz",
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}/chef-backup-#{backup_time}.tgz #{export_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
- private_chef['backup']['always_dump_db']
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