chef_backup 0.0.1.dev.2 → 0.1.1

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