chef_backup 0.0.1.dev.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +23 -0
  3. data/.kitchen.yml +30 -0
  4. data/.rubocop.yml +17 -0
  5. data/.travis.yml +6 -0
  6. data/Gemfile +4 -0
  7. data/Guardfile +22 -0
  8. data/LICENSE +13 -0
  9. data/README.md +33 -0
  10. data/Rakefile +44 -0
  11. data/chef_backup.gemspec +33 -0
  12. data/lib/chef_backup.rb +12 -0
  13. data/lib/chef_backup/config.rb +57 -0
  14. data/lib/chef_backup/data_map.rb +44 -0
  15. data/lib/chef_backup/exceptions.rb +12 -0
  16. data/lib/chef_backup/helpers.rb +159 -0
  17. data/lib/chef_backup/logger.rb +39 -0
  18. data/lib/chef_backup/runner.rb +136 -0
  19. data/lib/chef_backup/strategy.rb +29 -0
  20. data/lib/chef_backup/strategy/backup/custom.rb +7 -0
  21. data/lib/chef_backup/strategy/backup/ebs.rb +28 -0
  22. data/lib/chef_backup/strategy/backup/lvm.rb +42 -0
  23. data/lib/chef_backup/strategy/backup/object.rb +29 -0
  24. data/lib/chef_backup/strategy/backup/tar.rb +165 -0
  25. data/lib/chef_backup/strategy/restore/custom.rb +0 -0
  26. data/lib/chef_backup/strategy/restore/ebs.rb +0 -0
  27. data/lib/chef_backup/strategy/restore/lvm.rb +0 -0
  28. data/lib/chef_backup/strategy/restore/object.rb +0 -0
  29. data/lib/chef_backup/strategy/restore/tar.rb +125 -0
  30. data/lib/chef_backup/version.rb +4 -0
  31. data/spec/fixtures/chef-server-running.json +584 -0
  32. data/spec/spec_helper.rb +103 -0
  33. data/spec/unit/data_map_spec.rb +59 -0
  34. data/spec/unit/helpers_spec.rb +88 -0
  35. data/spec/unit/runner_spec.rb +185 -0
  36. data/spec/unit/shared_examples/helpers.rb +20 -0
  37. data/spec/unit/strategy/backup/lvm_spec.rb +0 -0
  38. data/spec/unit/strategy/backup/shared_examples/backup.rb +74 -0
  39. data/spec/unit/strategy/backup/tar_spec.rb +280 -0
  40. data/spec/unit/strategy/restore/lvm_spec.rb +0 -0
  41. data/spec/unit/strategy/restore/shared_examples/restore.rb +84 -0
  42. data/spec/unit/strategy/restore/tar_spec.rb +238 -0
  43. data/spec/unit/strategy_spec.rb +36 -0
  44. metadata +253 -0
File without changes
@@ -0,0 +1,74 @@
1
+ require 'spec_helper'
2
+
3
+ shared_examples 'a tar based backup' do
4
+ it 'populates the data map with services and configs' do
5
+ expect(subject).to receive(:populate_data_map).once
6
+ subject.backup
7
+ end
8
+
9
+ it 'creates a backup manifest' do
10
+ expect(subject).to receive(:write_manifest).once
11
+ subject.backup
12
+ end
13
+
14
+ it 'creates a tarball of the backup' do
15
+ expect(subject).to receive(:create_tarball).once
16
+ subject.backup
17
+ end
18
+
19
+ it 'cleans up the temp directory' do
20
+ expect(subject).to receive(:cleanup).at_least(:once)
21
+ subject.backup
22
+ end
23
+ end
24
+
25
+ shared_examples 'a tar based frontend' do
26
+ it 'doesnt stop any services' do
27
+ expect(subject).to_not receive(:stop_service)
28
+ subject.backup
29
+ end
30
+
31
+ it 'doesnt dump the db' do
32
+ expect(subject).to_not receive(:dump_db)
33
+ subject.backup
34
+ end
35
+ end
36
+
37
+ shared_examples 'a tar based online backend' do
38
+ it "doesn't start any services" do
39
+ expect(subject).to_not receive(:start_service)
40
+ subject.backup
41
+ end
42
+
43
+ it "doesn't stop any services" do
44
+ expect(subject).to_not receive(:stop_service)
45
+ subject.backup
46
+ end
47
+
48
+ it 'dumps the db' do
49
+ expect(subject).to receive(:dump_db).once
50
+ subject.backup
51
+ end
52
+ end
53
+
54
+ shared_examples 'a tar based offline backend' do
55
+ it 'stops all services besides keepalived and postgres' do
56
+ expect(subject).to receive(:stop_chef_server).once
57
+
58
+ %w(postgresql keepalived).each do |service|
59
+ expect(subject).to_not receive(:stop_service).with(service)
60
+ end
61
+
62
+ subject.backup
63
+ end
64
+
65
+ it 'starts all the services again' do
66
+ expect(subject).to receive(:start_chef_server).at_least(:once)
67
+ subject.backup
68
+ end
69
+
70
+ it 'dumps the db' do
71
+ expect(subject).to receive(:dump_db).once
72
+ subject.backup
73
+ end
74
+ end
@@ -0,0 +1,280 @@
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 standlone' 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).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
+ end
File without changes
@@ -0,0 +1,84 @@
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
@@ -0,0 +1,238 @@
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