chef_backup 0.0.1.dev.4 → 0.2.0.pre1

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/config.rb +22 -12
  3. data/lib/chef_backup/data_map.rb +18 -12
  4. data/lib/chef_backup/deep_merge.rb +145 -0
  5. data/lib/chef_backup/helpers.rb +154 -28
  6. data/lib/chef_backup/logger.rb +11 -6
  7. data/lib/chef_backup/mash.rb +226 -0
  8. data/lib/chef_backup/runner.rb +24 -21
  9. data/lib/chef_backup/strategy/backup/custom.rb +1 -2
  10. data/lib/chef_backup/strategy/backup/ebs.rb +3 -6
  11. data/lib/chef_backup/strategy/backup/lvm.rb +2 -4
  12. data/lib/chef_backup/strategy/backup/object.rb +2 -4
  13. data/lib/chef_backup/strategy/backup/tar.rb +96 -40
  14. data/lib/chef_backup/strategy/restore/tar.rb +81 -51
  15. data/lib/chef_backup/strategy.rb +10 -10
  16. data/lib/chef_backup/version.rb +1 -1
  17. data/lib/chef_backup.rb +8 -8
  18. metadata +21 -162
  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 -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 -92
  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,294 +0,0 @@
1
- require 'spec_helper'
2
- require 'chef/mixin/deep_merge'
3
- require_relative 'shared_examples/backup'
4
-
5
- describe ChefBackup::Strategy::TarBackup do
6
- set_common_variables
7
-
8
- subject { described_class.new }
9
-
10
- before do
11
- use_default_running_config
12
- allow(subject).to receive(:tmp_dir).and_return(tmp_dir)
13
- allow(subject).to receive(:backup_time).and_return(backup_time)
14
- end
15
-
16
- describe '.backup' do
17
- before do
18
- %i(write_manifest dump_db stop_service create_tarball cleanup
19
- start_service export_tarball
20
- ).each do |method|
21
- allow(subject).to receive(method).and_return(true)
22
- end
23
-
24
- allow(subject).to receive(:enabled_services).and_return(enabled_services)
25
- allow(subject).to receive(:all_services).and_return(all_services)
26
- allow(subject).to receive(:dump_db).and_return(true)
27
- end
28
-
29
- context 'on a frontend' do
30
- before { private_chef('role' => 'frontend') }
31
-
32
- it_behaves_like 'a tar based backup'
33
- it_behaves_like 'a tar based frontend'
34
- end
35
-
36
- context 'on a backend' do
37
- before { private_chef('role' => 'backend') }
38
-
39
- context 'during an online backup' do
40
- before do
41
- private_chef('role' => 'backend', 'backup' => { 'mode' => 'online' })
42
- end
43
-
44
- it_behaves_like 'a tar based backup'
45
- it_behaves_like 'a tar based online backend'
46
- end
47
-
48
- context 'during an offline backup' do
49
- it_behaves_like 'a tar based backup'
50
- it_behaves_like 'a tar based offline backend'
51
- end
52
-
53
- context 'when no mode is configured' do
54
- before do
55
- private_chef('role' => 'backend', 'backup' => { 'mode' => nil })
56
- end
57
-
58
- it_behaves_like 'a tar based backup'
59
- it_behaves_like 'a tar based offline backend'
60
- end
61
- end
62
-
63
- context 'on a standalone' do
64
- before { private_chef('role' => 'standalone') }
65
-
66
- context 'during an online backup' do
67
- before do
68
- private_chef('role' => 'standalone',
69
- 'backup' => { 'mode' => 'online' })
70
- end
71
-
72
- it_behaves_like 'a tar based backup'
73
- it_behaves_like 'a tar based online backend'
74
- end
75
-
76
- context 'during an offline backup' do
77
- it_behaves_like 'a tar based backup'
78
- it_behaves_like 'a tar based offline backend'
79
- end
80
-
81
- context 'when no mode is configured' do
82
- before do
83
- private_chef('role' => 'standalone', 'backup' => { 'mode' => nil })
84
- end
85
-
86
- it_behaves_like 'a tar based backup'
87
- it_behaves_like 'a tar based offline backend'
88
- end
89
- end
90
- end
91
-
92
- describe '.dump_db' do
93
- let(:dump_cmd) do
94
- ['/opt/opscode/embedded/bin/chpst',
95
- '-u opscode-pgsql',
96
- '/opt/opscode/embedded/bin/pg_dumpall',
97
- "> #{tmp_dir}/chef_backup-#{backup_time}.sql"
98
- ].join(' ')
99
- end
100
-
101
- let(:tmp_dir) { '/tmp/notaswear' }
102
- let(:backup_time) { Time.now }
103
-
104
- before do
105
- allow(subject).to receive(:tmp_dir).and_return(tmp_dir)
106
- allow(subject).to receive(:backup_time).and_return(backup_time)
107
- allow(subject).to receive(:shell_out!).with(dump_cmd).and_return(true)
108
- private_chef('postgresql' => { 'username' => 'opscode-pgsql' })
109
- subject.data_map.add_service('postgresql', '/data/dir')
110
- end
111
-
112
- %w(backend standalone).each do |role|
113
- context "on a #{role}" do
114
- before do
115
- private_chef('role' => role)
116
- end
117
-
118
- it 'dumps the db' do
119
- expect(subject).to receive(:shell_out!).with(dump_cmd)
120
- subject.dump_db
121
- end
122
-
123
- it 'updates the data map' do
124
- subject.dump_db
125
- expect(subject.data_map.services['postgresql'])
126
- .to include('pg_dump_success' => true)
127
- end
128
-
129
- it 'adds the postgresql username to the data map' do
130
- subject.dump_db
131
- expect(subject.data_map.services['postgresql'])
132
- .to include('username' => 'opscode-pgsql')
133
- end
134
- end
135
- end
136
-
137
- context 'on a frontend' do
138
- before { private_chef('role' => 'frontend') }
139
-
140
- it "doesn't dump the db" do
141
- expect(subject).to_not receive(:shell_out).with(/pg_dumpall/)
142
- subject.dump_db
143
- end
144
- end
145
- end
146
-
147
- describe '.create_tarball' do
148
- before do
149
- allow(subject).to receive(:data_map).and_return(data_map)
150
- allow(Dir).to receive(:[]).and_return(%w(sql.sql manifest.json))
151
- end
152
-
153
- it 'creates a tarball with all items in the temp directory' do
154
- cmd = [
155
- "tar -czf #{tmp_dir}/chef-backup-#{backup_time}.tgz",
156
- data_map.services.map { |_, v| v['data_dir'] }.compact.join(' '),
157
- data_map.configs.map { |_, v| v['data_dir'] }.compact.join(' '),
158
- Dir["#{tmp_dir}/*"].map { |f| File.basename(f) }.join(' ')
159
- ].join(' ').strip
160
-
161
- allow(subject).to receive(:shell_out).with(cmd, cdw: tmp_dir)
162
- expect(subject).to receive(:shell_out).with(cmd, cwd: tmp_dir)
163
- subject.create_tarball
164
- end
165
- end
166
-
167
- describe '.export_tarball' do
168
- before do
169
- allow(subject).to receive(:export_dir).and_return('/mnt/chef-backups')
170
- end
171
-
172
- it 'moves the tarball to the archive location' do
173
- cmd = "rsync -chaz #{tmp_dir}/chef-backup-#{backup_time}.tgz"
174
- cmd << " #{export_dir}/"
175
-
176
- allow(subject).to receive(:shell_out).with(cmd)
177
- expect(subject).to receive(:shell_out).with(cmd)
178
- subject.export_tarball
179
- end
180
- end
181
-
182
- describe '.write_manifest' do
183
- let(:manifest) do
184
- { 'some' => {
185
- 'nested' => {
186
- 'hash' => true
187
- },
188
- 'another' => true
189
- }
190
- }
191
- end
192
-
193
- let(:file) { double('file', write: true) }
194
-
195
- before do
196
- allow(subject).to receive(:manifest).and_return(manifest)
197
- allow(subject).to receive(:tmp_dir).and_return(tmp_dir)
198
- allow(File).to receive(:open).and_yield(file)
199
- end
200
-
201
- it 'converts the manifest to json' do
202
- json_manifest = JSON.pretty_generate(subject.manifest)
203
- expect(file).to receive(:write).with(json_manifest)
204
- subject.write_manifest
205
- end
206
-
207
- it 'writes a json file to the tmp_dir' do
208
- expect(File).to receive(:open).with("#{tmp_dir}/manifest.json", 'w')
209
- subject.write_manifest
210
- end
211
- end
212
-
213
- describe '.populate_data_map' do
214
- let(:services) { %w(opscode-solr4 bookshelf rabbitmq) }
215
- let(:configs) { %w(opscode opscode-manage opscode-analytics) }
216
- let(:config) do
217
- { 'bookshelf' => { 'data_dir' => '/bookshelf/data' },
218
- 'opscode-solr4' => { 'data_dir' => '/solr4/data' },
219
- 'rabbitmq' => { 'data_dir' => '/rabbitmq/data' }
220
- }
221
- end
222
-
223
- before do
224
- allow(subject).to receive(:data_map).and_return(data_map)
225
- allow(subject).to receive(:stateful_services).and_return(services)
226
- allow(subject).to receive(:config_directories).and_return(configs)
227
- %w(add_service add_config add_ha_info).each do |method|
228
- allow(data_map).to receive(method.to_sym).and_return(true)
229
- end
230
- end
231
-
232
- %w(frontend backend standalone).each do |role|
233
- context "on a #{role}" do
234
- before { private_chef(config.merge('role' => role)) }
235
-
236
- it 'populates the data map with config directories' do
237
- configs.each do |config|
238
- expect(subject.data_map)
239
- .to receive(:add_config)
240
- .with(config, "/etc/#{config}")
241
- end
242
-
243
- subject.populate_data_map
244
- end
245
- end
246
- end
247
-
248
- %w(backend standalone).each do |role|
249
- context "on a #{role}" do
250
- before { private_chef(config.merge('role' => role)) }
251
-
252
- it 'populates the data map with service directories' do
253
- services.each do |service|
254
- expect(subject.data_map)
255
- .to receive(:add_service)
256
- .with(service, config[service]['data_dir'])
257
- end
258
-
259
- subject.populate_data_map
260
- end
261
-
262
- it 'populates the data map with the upgrades' do
263
- expect(subject.data_map)
264
- .to receive(:add_service)
265
- .with('upgrades', '/var/opt/opscode/upgrades')
266
-
267
- subject.populate_data_map
268
- end
269
- end
270
- end
271
-
272
- context 'on a frontend' do
273
- before { private_chef(config.merge('role' => 'frontend')) }
274
-
275
- it "doesn't populate the data map with the services" do
276
- expect(subject.data_map).to_not receive(:add_service)
277
- end
278
- end
279
- end
280
-
281
- describe '.pg_dump?' do
282
- it 'returns true' do
283
- expect(subject.pg_dump?).to eq(true)
284
- end
285
-
286
- context 'when db dump is disabled' do
287
- before { private_chef('backup' => { 'always_dump_db' => false }) }
288
-
289
- it 'returns false' do
290
- expect(subject.pg_dump?).to eq(false)
291
- end
292
- end
293
- end
294
- end
File without changes
@@ -1,84 +0,0 @@
1
- require 'spec_helper'
2
-
3
- shared_examples 'a tar based restore' do
4
- it "cleanse's the chef server" do
5
- expect(subject).to receive(:cleanse_chef_server).once
6
- subject.restore
7
- end
8
-
9
- it 'restores the configs' do
10
- configs.each do |config|
11
- expect(subject).to receive(:restore_data).with(:configs, config).once
12
- end
13
- subject.restore
14
- end
15
-
16
- it 'touches the bootstrap sentinel file' do
17
- expect(subject).to receive(:touch_sentinel).once
18
- subject.restore
19
- end
20
-
21
- it 'reconfigures the server' do
22
- expect(subject).to receive(:reconfigure_server).once
23
- subject.restore
24
- end
25
-
26
- it 'updates the config' do
27
- expect(subject).to receive(:update_config).once
28
- subject.restore
29
- end
30
-
31
- it 'starts the server' do
32
- expect(subject).to receive(:start_chef_server).once
33
- subject.restore
34
- end
35
-
36
- it 'cleans up the temp directory' do
37
- expect(subject).to receive(:cleanup).once
38
- subject.restore
39
- end
40
- end
41
-
42
- shared_examples 'a tar based frontend restore' do
43
- it 'does not restore services' do
44
- expect(subject).to_not receive(:restore_services)
45
- subject.restore
46
- end
47
-
48
- it 'does not start postgres' do
49
- expect(subject).to_not receive(:start_service).with(:postgresql)
50
- subject.restore
51
- end
52
-
53
- it 'does not attempt to import a database' do
54
- expect(subject).to_not receive(:import_db)
55
- subject.restore
56
- end
57
- end
58
-
59
- shared_examples 'a tar based backend restore' do
60
- it 'restores the stateful services' do
61
- services.each do |service|
62
- expect(subject)
63
- .to receive(:restore_data)
64
- .with(:services, service)
65
- .once
66
- end
67
- subject.restore
68
- end
69
- end
70
-
71
- shared_examples 'a tar based backend restore with db dump' do
72
- it 'restores the db dump' do
73
- expect(subject).to receive(:import_db)
74
- subject.restore
75
- end
76
- end
77
-
78
- shared_examples 'a tar based backend restore without db dump' do
79
- it 'does not try to import a db dump' do
80
- expect(subject).to_not receive(:import_db)
81
- expect(subject).to_not receive(:start_service).with(:postgresql)
82
- subject.restore
83
- end
84
- end
@@ -1,238 +0,0 @@
1
- require 'spec_helper'
2
- require_relative 'shared_examples/restore'
3
-
4
- describe ChefBackup::Strategy::TarRestore do
5
- let(:manifest) do
6
- { 'strategy' => 'tar',
7
- 'backup_time' => '2014-12-02-22-46-58',
8
- 'services' => {
9
- 'rabbitmq' => { 'data_dir' => '/var/opt/opscode/rabbitmq/db' },
10
- 'opscode-solr4' => {
11
- 'data_dir' => '/var/opt/opscode/opscode-solr4/data'
12
- },
13
- 'redis_lb' => { 'data_dir' => '/var/opt/opscode/redis_lb/data' },
14
- 'postgresql' => {
15
- 'data_dir' => '/var/opt/opscode/postgresql/9.2/data',
16
- 'pg_dump_success' => pg_dump_success,
17
- 'username' => 'opscode-pgsql'
18
- },
19
- 'bookshelf' => { 'data_dir' => '/var/opt/opscode/bookshelf/data' }
20
- },
21
- 'configs' => {
22
- 'opscode' => { 'data_dir' => '/etc/opscode' },
23
- 'opscode-manage' => { 'data_dir' => '/etc/opscode-manage' },
24
- 'opscode-reporting' => { 'data_dir' => '/etc/opscode-reporting' },
25
- 'opscode-analytics' => { 'data_dir' => '/etc/opscode-analytics' }
26
- }
27
- }
28
- end
29
-
30
- let(:pg_dump_success) { true }
31
- let(:tarball_path) { '/var/backups/chef-backup-2014-12-02-22-46-58.tgz' }
32
- let(:configs) { manifest['configs'].keys }
33
- let(:services) { manifest['services'].keys }
34
- let(:restore_dir) { ChefBackup::Config['restore_dir'] }
35
-
36
- subject { described_class.new(tarball_path) }
37
-
38
- before(:each) { use_default_cli_args }
39
-
40
- describe '.restore' do
41
- before do
42
- %i(shell_out shell_out! unpack_tarball stop_chef_server ensure_file!
43
- start_chef_server reconfigure_server cleanse_chef_server
44
- update_config import_db touch_sentinel
45
- ).each do |method|
46
- allow(subject).to receive(method).and_return(true)
47
- end
48
- configs.each do |config|
49
- allow(subject)
50
- .to receive(:restore_data)
51
- .with(:configs, config)
52
- .and_return(true)
53
- end
54
- services.each do |service|
55
- allow(subject)
56
- .to receive(:restore_data)
57
- .with(:services, service)
58
- .and_return(true)
59
- end
60
-
61
- allow(subject).to receive(:tarball_path).and_return(tarball_path)
62
- allow(subject).to receive(:manifest).and_return(manifest)
63
- end
64
-
65
- it_behaves_like 'a tar based restore'
66
-
67
- context 'on a frontend' do
68
- before do
69
- allow(subject).to receive(:frontend?).and_return(true)
70
- end
71
-
72
- it_behaves_like 'a tar based frontend restore'
73
- end
74
-
75
- context 'on a backend' do
76
- before do
77
- allow(subject).to receive(:frontend?).and_return(false)
78
- end
79
-
80
- it_behaves_like 'a tar based backend restore'
81
-
82
- context 'when a db dump is present' do
83
- before do
84
- allow(subject).to receive(:restore_db_dump?).and_return(true)
85
- allow(subject)
86
- .to receive(:start_service).with(:postgresql).and_return(true)
87
- allow(subject).to receive(:import_db).and_return(true)
88
- end
89
-
90
- it_behaves_like 'a tar based backend restore with db dump'
91
- end
92
-
93
- context 'when a db dump is not present' do
94
- before do
95
- allow(subject).to receive(:restore_db_dump?).and_return(false)
96
- end
97
-
98
- it_behaves_like 'a tar based backend restore without db dump'
99
- end
100
- end
101
-
102
- context 'on a standalone' do
103
- before do
104
- allow(subject).to receive(:frontend?).and_return(false)
105
- end
106
-
107
- it_behaves_like 'a tar based backend restore'
108
-
109
- context 'when a db dump is present' do
110
- before do
111
- allow(subject).to receive(:restore_db_dump?).and_return(true)
112
- end
113
-
114
- it_behaves_like 'a tar based backend restore with db dump'
115
- end
116
-
117
- context 'when a db dump is not present' do
118
- before do
119
- allow(subject).to receive(:restore_db_dump?).and_return(false)
120
- end
121
-
122
- it_behaves_like 'a tar based backend restore without db dump'
123
- end
124
- end
125
- end
126
-
127
- describe '.manifest' do
128
- let(:json) { "{\"some\":\"json\"}" }
129
- let(:manifest_json) { File.join(restore_dir, 'manifest.json') }
130
-
131
- it 'parses the manifest from the restore dir' do
132
- allow(subject).to receive(:ensure_file!).and_return(true)
133
- allow(File).to receive(:read).with(manifest_json).and_return(json)
134
- expect(subject.manifest).to eq('some' => 'json')
135
- end
136
-
137
- it 'raises an error if the manifest is invalid' do
138
- expect { subject.manifest }
139
- .to raise_error(
140
- ChefBackup::Strategy::TarRestore::InvalidManifest,
141
- "#{File.join(restore_dir, 'manifest.json')} not found"
142
- )
143
- end
144
- end
145
-
146
- describe '.restore_data' do
147
- before do
148
- ChefBackup::Config['restore_dir'] = restore_dir
149
- allow(subject).to receive(:manifest).and_return(manifest)
150
- allow(subject).to receive(:shell_out!).and_return(true)
151
- allow(File).to receive(:directory?).and_return(true)
152
- end
153
-
154
- context 'with config data' do
155
- it 'rsyncs the config from the restore dir to the data_dir' do
156
- source = File.expand_path(
157
- File.join(restore_dir, manifest['configs']['opscode']['data_dir']))
158
- destination = manifest['configs']['opscode']['data_dir']
159
- cmd = "rsync -chaz --delete #{source}/ #{destination}"
160
-
161
- expect(subject).to receive(:shell_out!).with(cmd)
162
- subject.restore_data(:configs, 'opscode')
163
- end
164
- end
165
-
166
- context 'with service data' do
167
- it 'rsyncs the service from the restore dir to the data_dir' do
168
- source = File.expand_path(
169
- File.join(restore_dir, manifest['services']['rabbitmq']['data_dir']))
170
- destination = manifest['services']['rabbitmq']['data_dir']
171
- cmd = "rsync -chaz --delete #{source}/ #{destination}"
172
-
173
- expect(subject).to receive(:shell_out!).with(cmd)
174
- subject.restore_data(:services, 'rabbitmq')
175
- end
176
- end
177
- end
178
-
179
- describe '.import_db' do
180
- before do
181
- allow(subject).to receive(:manifest).and_return(manifest)
182
- allow(subject).to receive(:shell_out!).and_return(true)
183
- allow(subject).to receive(:running_config).and_return(running_config)
184
- allow(subject)
185
- .to receive(:start_service).with('postgresql').and_return(true)
186
- end
187
-
188
- context 'without a db dump' do
189
- it 'raises an exception' do
190
- expect { subject.import_db }
191
- .to raise_error(ChefBackup::Exceptions::InvalidDatabaseDump)
192
- end
193
- end
194
-
195
- context 'with a db dump' do
196
- let(:db_sql) do
197
- File.join(restore_dir, "chef_backup-#{manifest['backup_time']}.sql")
198
- end
199
-
200
- let(:import_cmd) do
201
- ['/opt/opscode/embedded/bin/chpst -u opscode-pgsql',
202
- '/opt/opscode/embedded/bin/psql -U opscode-pgsql',
203
- "-d opscode_chef < #{db_sql}"
204
- ].join(' ')
205
- end
206
-
207
- before do
208
- allow(subject)
209
- .to receive(:ensure_file!)
210
- .with(db_sql,
211
- ChefBackup::Exceptions::InvalidDatabaseDump,
212
- "#{db_sql} not found")
213
- .and_return(true)
214
- end
215
-
216
- it 'imports the database' do
217
- expect(subject).to receive(:shell_out!).with(import_cmd)
218
- subject.import_db
219
- end
220
- end
221
- end
222
-
223
- describe '.touch_sentinel' do
224
- let(:file) { double('File', write: true) }
225
- let(:file_dir) { '/var/opt/opscode' }
226
- let(:file_path) { File.join(file_dir, 'bootstrapped') }
227
-
228
- before do
229
- allow(FileUtils).to receive(:mkdir_p).with(file_dir).and_return(true)
230
- allow(File).to receive(:open).with(file_path, 'w').and_yield(file)
231
- end
232
-
233
- it 'touches the bootstrap sentinel file' do
234
- expect(file).to receive(:write).with('bootstrapped!')
235
- subject.touch_sentinel
236
- end
237
- end
238
- end
@@ -1,36 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe ChefBackup::Strategy do
4
- before do
5
- # Backup Tester
6
- class ChefBackup::Strategy::TestBackup
7
- def initialize(_p = {})
8
- end
9
- end
10
-
11
- # Restore Tester
12
- class ChefBackup::Strategy::TestRestore
13
- def initialize(_p = {})
14
- end
15
- end
16
- end
17
-
18
- after do
19
- described_class.send(:remove_const, :TestBackup)
20
- described_class.send(:remove_const, :TestRestore)
21
- end
22
-
23
- describe '.backup' do
24
- it 'it returns a backup strategy' do
25
- expect(described_class.backup('test'))
26
- .to be_an(ChefBackup::Strategy::TestBackup)
27
- end
28
- end
29
-
30
- describe '.restore' do
31
- it 'it returns a restore strategy' do
32
- expect(described_class.restore('test', '/some/backup.tgz'))
33
- .to be_an(ChefBackup::Strategy::TestRestore)
34
- end
35
- end
36
- end