do_snapshot 0.3.0 → 0.3.4
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.
- checksums.yaml +4 -4
- data/README.md +4 -0
- data/lib/do_snapshot/adapter/abstract.rb +32 -3
- data/lib/do_snapshot/adapter/digitalocean.rb +15 -15
- data/lib/do_snapshot/adapter/digitalocean_v2.rb +19 -15
- data/lib/do_snapshot/cli.rb +1 -1
- data/lib/do_snapshot/command.rb +10 -3
- data/lib/do_snapshot/version.rb +1 -1
- data/lib/do_snapshot.rb +20 -0
- data/spec/do_snapshot/adapter/abstract_spec.rb +1 -1
- data/spec/do_snapshot/adapter/digitalocean_spec.rb +30 -10
- data/spec/do_snapshot/adapter/digitalocean_v2_spec.rb +30 -11
- data/spec/do_snapshot/cli_spec.rb +1 -1
- data/spec/do_snapshot/command_spec.rb +139 -97
- data/spec/do_snapshot/configuration_spec.rb +1 -1
- data/spec/do_snapshot/log_spec.rb +1 -1
- data/spec/do_snapshot/mail_spec.rb +1 -1
- data/spec/do_snapshot/runner_spec.rb +1 -1
- data/spec/do_snapshot_spec.rb +21 -1
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4af572f41b36fbe4252628e2508404b789afca71
|
4
|
+
data.tar.gz: d31302ccbc40a5a24491ecefa788ea89b6f1881e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dcffac80be5524e1e54a6d2754aec6bde8c991bafe84d93b830d52b984f2830df03399333c687c5bc88d27786339e91e1077987741080b953922a7eb10d15f27
|
7
|
+
data.tar.gz: 7983504785434f31b8169280923c506f1581b63ee6a34132edaab245a853061246fdbe6a3e6e0eef2eeff43e9810d62ad22283f27b93d57fd9163d4440594325
|
data/README.md
CHANGED
@@ -185,6 +185,10 @@ For working mailer you need to set e-mail settings via run options.
|
|
185
185
|
|
186
186
|
You can optionally specify parameters to select or exclude some droplets.
|
187
187
|
|
188
|
+
## Donating:
|
189
|
+
Support this project and others by [merqlove](https://gratipay.com/~merqlove/) via [gratipay](https://gratipay.com/~merqlove/).
|
190
|
+
[](https://gratipay.com/merqlove/)
|
191
|
+
|
188
192
|
## Dependencies:
|
189
193
|
|
190
194
|
- [Thor](https://github.com/erikhuda/thor) for CLI.
|
@@ -25,10 +25,20 @@ module DoSnapshot
|
|
25
25
|
def check_keys; end
|
26
26
|
|
27
27
|
# Waiting for event exit
|
28
|
-
def
|
29
|
-
logger.debug
|
28
|
+
def wait_wrap(id, message = "Event Id: #{id}", &status_block)
|
29
|
+
logger.debug message
|
30
30
|
time = Time.now
|
31
|
-
sleep(delay) until
|
31
|
+
sleep(delay) until status_block.call(id, time)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Waiting for event exit
|
35
|
+
def wait_event(event_id)
|
36
|
+
wait_wrap(event_id) { |id, time| get_event_status(id, time) }
|
37
|
+
end
|
38
|
+
|
39
|
+
# Waiting for event exit
|
40
|
+
def wait_shutdown(droplet_id)
|
41
|
+
wait_wrap(droplet_id, "Droplet Id: #{droplet_id} shutting down") { |id, time| get_shutdown_status(id, time) }
|
32
42
|
end
|
33
43
|
|
34
44
|
def after_cleanup(droplet_id, droplet_name, snapshot, event)
|
@@ -40,6 +50,25 @@ module DoSnapshot
|
|
40
50
|
logger.debug "Snapshot name: #{snapshot.name} delete requested."
|
41
51
|
end
|
42
52
|
end
|
53
|
+
|
54
|
+
def timeout?(id, time, message = "Event #{id} finished by timeout #{time}")
|
55
|
+
return false unless (Time.now - time) > @timeout
|
56
|
+
logger.debug message
|
57
|
+
true
|
58
|
+
end
|
59
|
+
|
60
|
+
def droplet_timeout?(id, time)
|
61
|
+
timeout? id, time, "Droplet id: #{id} shutdown event closed by timeout #{time}"
|
62
|
+
end
|
63
|
+
|
64
|
+
# Looking for event status.
|
65
|
+
# Before snapshot we to know that machine has powered off.
|
66
|
+
#
|
67
|
+
def get_shutdown_status(id, time)
|
68
|
+
fail "Droplet #{id} not responding for shutdown!" if droplet_timeout?(id, time)
|
69
|
+
|
70
|
+
inactive?(id)
|
71
|
+
end
|
43
72
|
end
|
44
73
|
end
|
45
74
|
end
|
@@ -35,11 +35,9 @@ module DoSnapshot
|
|
35
35
|
# noinspection RubyResolve
|
36
36
|
instance = droplet(id)
|
37
37
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
power_on id
|
42
|
-
end
|
38
|
+
return power_on(id) unless instance.status.include? 'active'
|
39
|
+
|
40
|
+
logger.error "Droplet #{id} is still running. Skipping."
|
43
41
|
end
|
44
42
|
|
45
43
|
# Power Off request for Droplet
|
@@ -51,7 +49,7 @@ module DoSnapshot
|
|
51
49
|
fail event.message unless event.status.include? 'OK'
|
52
50
|
|
53
51
|
# noinspection RubyResolve
|
54
|
-
|
52
|
+
wait_shutdown(id)
|
55
53
|
rescue => e
|
56
54
|
raise DropletShutdownError.new(id), e.message, e.backtrace
|
57
55
|
end
|
@@ -63,15 +61,23 @@ module DoSnapshot
|
|
63
61
|
event = ::DigitaloceanC::Droplet.snapshot(id, name: name)
|
64
62
|
|
65
63
|
if !event
|
66
|
-
fail 'Something wrong with DigitalOcean or with your connection :)'
|
64
|
+
fail DoSnapshot::SnapshotCreateError.new(id), 'Something wrong with DigitalOcean or with your connection :)'
|
67
65
|
elsif event && !event.status.include?('OK')
|
68
|
-
fail event.message
|
66
|
+
fail DoSnapshot::SnapshotCreateError.new(id), event.message
|
69
67
|
end
|
70
68
|
|
71
69
|
# noinspection RubyResolve
|
72
70
|
wait_event(event.event_id)
|
73
71
|
end
|
74
72
|
|
73
|
+
# Checking if droplet is powered off.
|
74
|
+
#
|
75
|
+
def inactive?(id)
|
76
|
+
instance = droplet(id)
|
77
|
+
|
78
|
+
instance.status.include?('off')
|
79
|
+
end
|
80
|
+
|
75
81
|
# Cleanup our snapshots.
|
76
82
|
#
|
77
83
|
def cleanup_snapshots(instance, size)
|
@@ -107,17 +113,11 @@ module DoSnapshot
|
|
107
113
|
return true if timeout?(id, time)
|
108
114
|
|
109
115
|
event = ::DigitaloceanC::Event.find(id)
|
110
|
-
fail event.message unless event.status.include?('OK')
|
116
|
+
fail DoSnapshot::EventError.new(id), event.message unless event.status.include?('OK')
|
111
117
|
# noinspection RubyResolve,RubyResolve
|
112
118
|
event.event.percentage && event.event.percentage.include?('100') ? true : false
|
113
119
|
end
|
114
120
|
|
115
|
-
def timeout?(id, time)
|
116
|
-
return false unless (Time.now - time) > @timeout
|
117
|
-
logger.debug "Event #{id} finished by timeout #{time}"
|
118
|
-
true
|
119
|
-
end
|
120
|
-
|
121
121
|
# Request Power On for droplet
|
122
122
|
#
|
123
123
|
def power_on(id)
|
@@ -41,21 +41,19 @@ module DoSnapshot
|
|
41
41
|
# noinspection RubyResolve
|
42
42
|
instance = droplet(id)
|
43
43
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
power_on id
|
48
|
-
end
|
44
|
+
return power_on(id) unless instance.status && instance.status.include?('active')
|
45
|
+
|
46
|
+
logger.error "Droplet #{id} is still running. Skipping."
|
49
47
|
end
|
50
48
|
|
51
49
|
# Power Off request for Droplet
|
52
50
|
#
|
53
51
|
def stop_droplet(id)
|
54
52
|
# noinspection RubyResolve,RubyResolve
|
55
|
-
|
53
|
+
client.droplet_actions.power_off(droplet_id: id)
|
56
54
|
|
57
55
|
# noinspection RubyResolve
|
58
|
-
|
56
|
+
wait_shutdown(id)
|
59
57
|
rescue => e
|
60
58
|
raise DropletShutdownError.new(id), e.message, e.backtrace
|
61
59
|
end
|
@@ -66,10 +64,18 @@ module DoSnapshot
|
|
66
64
|
# noinspection RubyResolve,RubyResolve
|
67
65
|
event = client.droplet_actions.snapshot(droplet_id: id, name: name)
|
68
66
|
|
67
|
+
fail DoSnapshot::SnapshotCreateError.new(id), name unless event && event.respond_to?(:id)
|
68
|
+
|
69
69
|
# noinspection RubyResolve
|
70
70
|
wait_event(event.id)
|
71
|
-
|
72
|
-
|
71
|
+
end
|
72
|
+
|
73
|
+
# Checking if droplet is powered off.
|
74
|
+
#
|
75
|
+
def inactive?(id)
|
76
|
+
instance = droplet(id)
|
77
|
+
|
78
|
+
instance.status.include?('off')
|
73
79
|
end
|
74
80
|
|
75
81
|
# Cleanup our snapshots.
|
@@ -117,16 +123,14 @@ module DoSnapshot
|
|
117
123
|
# Before snapshot we to know that machine has powered off.
|
118
124
|
#
|
119
125
|
def get_event_status(id, time)
|
120
|
-
if (
|
121
|
-
logger.debug "Event #{id} finished by timeout #{time}"
|
122
|
-
return true
|
123
|
-
end
|
126
|
+
return true if timeout?(id, time)
|
124
127
|
|
125
128
|
action = client.actions.find(id: id)
|
129
|
+
|
130
|
+
fail DoSnapshot::EventError.new(id), 'Check your connection' unless action && action.respond_to?(:status)
|
131
|
+
|
126
132
|
# noinspection RubyResolve,RubyResolve
|
127
133
|
action.status.include?('completed') ? true : false
|
128
|
-
rescue => e
|
129
|
-
raise e.message, e.backtrace
|
130
134
|
end
|
131
135
|
|
132
136
|
# Request Power On for droplet
|
data/lib/do_snapshot/cli.rb
CHANGED
@@ -203,7 +203,7 @@ module DoSnapshot
|
|
203
203
|
config.logger_level = Logger::DEBUG if config.verbose
|
204
204
|
config.verbose = options['trace']
|
205
205
|
config.quiet = options['quiet']
|
206
|
-
config.mailer = Mail.new(opts: options['mail'], smtp: options['smtp'])
|
206
|
+
config.mailer = Mail.new(opts: options['mail'], smtp: options['smtp']) if options['mail']
|
207
207
|
end
|
208
208
|
end
|
209
209
|
|
data/lib/do_snapshot/command.rb
CHANGED
@@ -42,11 +42,17 @@ module DoSnapshot
|
|
42
42
|
def stop_droplet(droplet)
|
43
43
|
logger.debug 'Shutting down droplet.'
|
44
44
|
api.stop_droplet(droplet.id) unless droplet.status.include? 'off'
|
45
|
+
true
|
46
|
+
rescue => e
|
47
|
+
logger.error e.message
|
48
|
+
false
|
45
49
|
end
|
46
50
|
|
47
51
|
# Trying to create a snapshot.
|
48
52
|
#
|
49
53
|
def create_snapshot(droplet) # rubocop:disable MethodLength,Metrics/AbcSize
|
54
|
+
fail DropletPowerError.new(droplet.id), droplet.name unless api.inactive?(droplet.id)
|
55
|
+
|
50
56
|
logger.info "Start creating snapshot for droplet id: #{droplet.id} name: #{droplet.name}."
|
51
57
|
|
52
58
|
today = DateTime.now
|
@@ -69,6 +75,8 @@ module DoSnapshot
|
|
69
75
|
case e.class.to_s
|
70
76
|
when 'DoSnapshot::SnapshotCleanupError'
|
71
77
|
raise e.class, e.message, e.backtrace
|
78
|
+
when 'DoSnapshot::DropletPowerError'
|
79
|
+
return
|
72
80
|
else
|
73
81
|
raise SnapshotCreateError.new(droplet.id), e.message, e.backtrace
|
74
82
|
end
|
@@ -132,8 +140,7 @@ module DoSnapshot
|
|
132
140
|
#
|
133
141
|
def thread_runner(droplet)
|
134
142
|
threads << Thread.new do
|
135
|
-
|
136
|
-
create_snapshot droplet
|
143
|
+
create_snapshot droplet if stop_droplet(droplet)
|
137
144
|
end
|
138
145
|
end
|
139
146
|
|
@@ -141,7 +148,7 @@ module DoSnapshot
|
|
141
148
|
# Droplet instance must be powered off first!
|
142
149
|
#
|
143
150
|
def prepare_droplet(id, name)
|
144
|
-
logger.debug "Droplet id: #{id} name: #{name}
|
151
|
+
logger.debug "Droplet id: #{id} name: #{name}\n"
|
145
152
|
droplet = api.droplet id
|
146
153
|
|
147
154
|
return unless droplet
|
data/lib/do_snapshot/version.rb
CHANGED
data/lib/do_snapshot.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
# -*- encoding : utf-8 -*-
|
2
|
+
require 'active_support/multibyte' # ActiveSupport 3.2 & Mail gem fix to work together.
|
3
|
+
|
2
4
|
require_relative 'do_snapshot/version'
|
3
5
|
require_relative 'do_snapshot/configuration'
|
4
6
|
|
@@ -80,6 +82,24 @@ module DoSnapshot
|
|
80
82
|
end
|
81
83
|
end
|
82
84
|
|
85
|
+
# When Droplet not Powered Off!
|
86
|
+
#
|
87
|
+
class DropletPowerError < RequestError
|
88
|
+
def initialize(*args)
|
89
|
+
DoSnapshot.logger.error "Droplet id: #{args[0]} must be Powered Off!"
|
90
|
+
super
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# When Event is failed!
|
95
|
+
#
|
96
|
+
class EventError < RequestError
|
97
|
+
def initialize(*args)
|
98
|
+
DoSnapshot.logger.error "Event id: #{args[0]} is failed!"
|
99
|
+
super
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
83
103
|
# When Digital Ocean API cannot retrieve list of droplets.
|
84
104
|
# Sometimes it connection problem or DigitalOcean API maintenance.
|
85
105
|
#
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# -*- encoding : utf-8 -*-
|
2
2
|
require 'spec_helper'
|
3
3
|
|
4
|
-
describe DoSnapshot::Adapter::Digitalocean do
|
4
|
+
RSpec.describe DoSnapshot::Adapter::Digitalocean do
|
5
5
|
include_context 'spec'
|
6
6
|
include_context 'api_v1_helpers'
|
7
7
|
|
@@ -40,7 +40,7 @@ describe DoSnapshot::Adapter::Digitalocean do
|
|
40
40
|
stub_droplet_fail(droplet_id)
|
41
41
|
|
42
42
|
expect { instance.droplet(droplet_id) }
|
43
|
-
.to raise_error
|
43
|
+
.to raise_error(DoSnapshot::DropletFindError)
|
44
44
|
expect(DoSnapshot.logger.buffer)
|
45
45
|
.to include 'Droplet Not Found'
|
46
46
|
|
@@ -62,7 +62,7 @@ describe DoSnapshot::Adapter::Digitalocean do
|
|
62
62
|
it 'with error' do
|
63
63
|
stub_droplets_fail
|
64
64
|
|
65
|
-
expect { instance.droplets }.to raise_error
|
65
|
+
expect { instance.droplets }.to raise_error(DoSnapshot::DropletListError)
|
66
66
|
expect(DoSnapshot.logger.buffer)
|
67
67
|
.to include 'Droplet Listing is failed to retrieve'
|
68
68
|
|
@@ -91,7 +91,7 @@ describe DoSnapshot::Adapter::Digitalocean do
|
|
91
91
|
expect { instance.start_droplet(droplet_id) }
|
92
92
|
.not_to raise_error
|
93
93
|
expect(DoSnapshot.logger.buffer)
|
94
|
-
.to include
|
94
|
+
.to include "Droplet #{droplet_id} is still running. Skipping."
|
95
95
|
|
96
96
|
expect(a_request(:get, droplet_url))
|
97
97
|
.to have_been_made
|
@@ -101,7 +101,7 @@ describe DoSnapshot::Adapter::Digitalocean do
|
|
101
101
|
stub_droplet_fail(droplet_id)
|
102
102
|
|
103
103
|
expect { instance.start_droplet(droplet_id) }
|
104
|
-
.to raise_error
|
104
|
+
.to raise_error(DoSnapshot::DropletFindError)
|
105
105
|
|
106
106
|
expect(a_request(:get, droplet_url))
|
107
107
|
.to have_been_made
|
@@ -109,23 +109,27 @@ describe DoSnapshot::Adapter::Digitalocean do
|
|
109
109
|
end
|
110
110
|
|
111
111
|
describe '.stop_droplet' do
|
112
|
-
it 'with
|
112
|
+
it 'with success' do
|
113
113
|
stub_event_done(event_id)
|
114
114
|
stub_droplet_stop(droplet_id)
|
115
|
+
stub_droplet_inactive(droplet_id)
|
115
116
|
|
116
117
|
instance.stop_droplet(droplet_id)
|
117
118
|
|
118
119
|
expect(a_request(:get, droplet_stop_url))
|
119
120
|
.to have_been_made
|
120
|
-
expect(a_request(:get,
|
121
|
+
expect(a_request(:get, droplet_url))
|
121
122
|
.to have_been_made
|
122
123
|
end
|
123
124
|
|
124
125
|
it 'with error' do
|
125
126
|
stub_droplet_stop_fail(droplet_id)
|
127
|
+
stub_droplet(droplet_id)
|
126
128
|
|
129
|
+
instance.timeout = 1
|
127
130
|
expect { instance.stop_droplet(droplet_id) }
|
128
|
-
.to raise_error
|
131
|
+
.to raise_error(DoSnapshot::DropletShutdownError)
|
132
|
+
instance.timeout = timeout
|
129
133
|
expect(DoSnapshot.logger.buffer)
|
130
134
|
.to include 'Droplet id: 100823 is Failed to Power Off.'
|
131
135
|
|
@@ -152,7 +156,7 @@ describe DoSnapshot::Adapter::Digitalocean do
|
|
152
156
|
stub_droplet_snapshot_fail(droplet_id, snapshot_name)
|
153
157
|
|
154
158
|
expect { instance.create_snapshot(droplet_id, snapshot_name) }
|
155
|
-
.to raise_error
|
159
|
+
.to raise_error(DoSnapshot::SnapshotCreateError)
|
156
160
|
|
157
161
|
expect(a_request(:get, droplet_snapshot_url))
|
158
162
|
.to have_been_made
|
@@ -163,7 +167,7 @@ describe DoSnapshot::Adapter::Digitalocean do
|
|
163
167
|
stub_event_fail(event_id)
|
164
168
|
|
165
169
|
expect { instance.create_snapshot(droplet_id, snapshot_name) }
|
166
|
-
.to raise_error
|
170
|
+
.to raise_error(DoSnapshot::EventError)
|
167
171
|
|
168
172
|
expect(a_request(:get, droplet_snapshot_url))
|
169
173
|
.to have_been_made
|
@@ -172,6 +176,22 @@ describe DoSnapshot::Adapter::Digitalocean do
|
|
172
176
|
end
|
173
177
|
end
|
174
178
|
|
179
|
+
describe '.inactive?' do
|
180
|
+
it 'when inactive' do
|
181
|
+
stub_droplet_inactive(droplet_id)
|
182
|
+
|
183
|
+
expect(instance.inactive?(droplet_id))
|
184
|
+
.to be_truthy
|
185
|
+
end
|
186
|
+
|
187
|
+
it 'when active' do
|
188
|
+
stub_droplet(droplet_id)
|
189
|
+
|
190
|
+
expect(instance.inactive?(droplet_id))
|
191
|
+
.to be_falsey
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
175
195
|
describe '.cleanup_snapshots' do
|
176
196
|
it 'with success' do
|
177
197
|
stub_droplet(droplet_id)
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# -*- encoding : utf-8 -*-
|
2
2
|
require 'spec_helper'
|
3
3
|
|
4
|
-
describe DoSnapshot::Adapter::DigitaloceanV2 do
|
4
|
+
RSpec.describe DoSnapshot::Adapter::DigitaloceanV2 do
|
5
5
|
include_context 'spec'
|
6
6
|
include_context 'api_v2_helpers'
|
7
7
|
|
@@ -42,7 +42,7 @@ describe DoSnapshot::Adapter::DigitaloceanV2 do
|
|
42
42
|
stub_droplet_fail(droplet_id)
|
43
43
|
|
44
44
|
expect { instance.droplet(droplet_id) }
|
45
|
-
.to raise_error
|
45
|
+
.to raise_error(DoSnapshot::DropletFindError)
|
46
46
|
expect(DoSnapshot.logger.buffer)
|
47
47
|
.to include 'Droplet Not Found'
|
48
48
|
|
@@ -64,7 +64,7 @@ describe DoSnapshot::Adapter::DigitaloceanV2 do
|
|
64
64
|
it 'with error' do
|
65
65
|
stub_droplets_fail
|
66
66
|
|
67
|
-
expect { instance.droplets }.to raise_error
|
67
|
+
expect { instance.droplets }.to raise_error(DoSnapshot::DropletListError)
|
68
68
|
expect(DoSnapshot.logger.buffer)
|
69
69
|
.to include 'Droplet Listing is failed to retrieve'
|
70
70
|
|
@@ -93,7 +93,7 @@ describe DoSnapshot::Adapter::DigitaloceanV2 do
|
|
93
93
|
expect { instance.start_droplet(droplet_id) }
|
94
94
|
.not_to raise_error
|
95
95
|
expect(DoSnapshot.logger.buffer)
|
96
|
-
.to include
|
96
|
+
.to include "Droplet #{droplet_id} is still running. Skipping."
|
97
97
|
|
98
98
|
expect(a_request(:get, droplet_url))
|
99
99
|
.to have_been_made
|
@@ -103,7 +103,7 @@ describe DoSnapshot::Adapter::DigitaloceanV2 do
|
|
103
103
|
stub_droplet_fail(droplet_id)
|
104
104
|
|
105
105
|
expect { instance.start_droplet(droplet_id) }
|
106
|
-
.to raise_error
|
106
|
+
.to raise_error(DoSnapshot::DropletFindError)
|
107
107
|
|
108
108
|
expect(a_request(:get, droplet_url))
|
109
109
|
.to have_been_made
|
@@ -111,23 +111,26 @@ describe DoSnapshot::Adapter::DigitaloceanV2 do
|
|
111
111
|
end
|
112
112
|
|
113
113
|
describe '.stop_droplet' do
|
114
|
-
it 'with
|
114
|
+
it 'with success' do
|
115
115
|
stub_event_done(event_id)
|
116
116
|
stub_droplet_stop(droplet_id)
|
117
|
+
stub_droplet_inactive(droplet_id)
|
117
118
|
|
118
119
|
instance.stop_droplet(droplet_id)
|
119
120
|
|
120
121
|
expect(a_request(:post, droplet_stop_url))
|
121
122
|
.to have_been_made
|
122
|
-
expect(a_request(:get,
|
123
|
+
expect(a_request(:get, droplet_url))
|
123
124
|
.to have_been_made
|
124
125
|
end
|
125
126
|
|
126
127
|
it 'with error' do
|
127
128
|
stub_droplet_stop_fail(droplet_id)
|
128
|
-
|
129
|
+
stub_droplet(droplet_id)
|
130
|
+
instance.timeout = 1
|
129
131
|
expect { instance.stop_droplet(droplet_id) }
|
130
|
-
.to raise_error
|
132
|
+
.to raise_error(DoSnapshot::DropletShutdownError)
|
133
|
+
instance.timeout = timeout
|
131
134
|
expect(DoSnapshot.logger.buffer)
|
132
135
|
.to include 'Droplet id: 100823 is Failed to Power Off.'
|
133
136
|
|
@@ -154,7 +157,7 @@ describe DoSnapshot::Adapter::DigitaloceanV2 do
|
|
154
157
|
stub_droplet_snapshot_fail(droplet_id, snapshot_name)
|
155
158
|
|
156
159
|
expect { instance.create_snapshot(droplet_id, snapshot_name) }
|
157
|
-
.to raise_error
|
160
|
+
.to raise_error(DoSnapshot::SnapshotCreateError)
|
158
161
|
|
159
162
|
expect(a_request(:post, droplet_snapshot_url))
|
160
163
|
.to have_been_made
|
@@ -165,7 +168,7 @@ describe DoSnapshot::Adapter::DigitaloceanV2 do
|
|
165
168
|
stub_event_fail(event_id)
|
166
169
|
|
167
170
|
expect { instance.create_snapshot(droplet_id, snapshot_name) }
|
168
|
-
.to raise_error
|
171
|
+
.to raise_error(DoSnapshot::EventError)
|
169
172
|
|
170
173
|
expect(a_request(:post, droplet_snapshot_url))
|
171
174
|
.to have_been_made
|
@@ -174,6 +177,22 @@ describe DoSnapshot::Adapter::DigitaloceanV2 do
|
|
174
177
|
end
|
175
178
|
end
|
176
179
|
|
180
|
+
describe '.inactive?' do
|
181
|
+
it 'when inactive' do
|
182
|
+
stub_droplet_inactive(droplet_id)
|
183
|
+
|
184
|
+
expect(instance.inactive?(droplet_id))
|
185
|
+
.to be_truthy
|
186
|
+
end
|
187
|
+
|
188
|
+
it 'when active' do
|
189
|
+
stub_droplet(droplet_id)
|
190
|
+
|
191
|
+
expect(instance.inactive?(droplet_id))
|
192
|
+
.to be_falsey
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
177
196
|
describe '.cleanup_snapshots' do
|
178
197
|
it 'with success' do
|
179
198
|
stub_droplet(droplet_id)
|
@@ -1,137 +1,179 @@
|
|
1
1
|
# -*- encoding : utf-8 -*-
|
2
2
|
require 'spec_helper'
|
3
3
|
|
4
|
-
describe DoSnapshot::Command do
|
4
|
+
RSpec.describe DoSnapshot::Command do
|
5
5
|
include_context 'spec'
|
6
6
|
include_context 'uri_helpers'
|
7
|
-
include_context 'api_v1_helpers'
|
8
7
|
|
9
8
|
subject(:cmd) { DoSnapshot::Command.new }
|
10
9
|
subject(:log) { DoSnapshot::Log }
|
11
10
|
|
12
|
-
describe '
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
11
|
+
describe 'V2' do
|
12
|
+
include_context 'api_v2_helpers'
|
13
|
+
end
|
14
|
+
|
15
|
+
describe 'V1' do
|
16
|
+
include_context 'api_v1_helpers'
|
17
|
+
|
18
|
+
describe '.snap' do
|
19
|
+
context 'when success' do
|
20
|
+
it 'sends message' do
|
21
|
+
stub_droplet_inactive(droplet_id)
|
22
|
+
expect { snap_runner }
|
23
|
+
.not_to raise_error
|
24
|
+
expect(DoSnapshot.logger.buffer)
|
25
|
+
.to include 'All operations has been finished.'
|
26
|
+
end
|
19
27
|
end
|
20
|
-
end
|
21
28
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
29
|
+
context 'when snapshot not cleanup' do
|
30
|
+
it 'sends message' do
|
31
|
+
stub_droplet_inactive(droplet_id)
|
32
|
+
stub_image_destroy_fail(image_id)
|
33
|
+
stub_image_destroy_fail(image_id2)
|
26
34
|
|
27
|
-
|
28
|
-
|
35
|
+
expect { snap_runner }
|
36
|
+
.not_to raise_error
|
37
|
+
end
|
29
38
|
end
|
30
|
-
end
|
31
39
|
|
32
|
-
|
33
|
-
|
34
|
-
|
40
|
+
context 'when droplet not found' do
|
41
|
+
it 'raised by exception' do
|
42
|
+
stub_droplet_fail(droplet_id)
|
35
43
|
|
36
|
-
|
37
|
-
|
44
|
+
expect { snap_runner }
|
45
|
+
.to raise_error(DoSnapshot::DropletFindError)
|
46
|
+
end
|
38
47
|
end
|
39
|
-
end
|
40
48
|
|
41
|
-
|
42
|
-
|
43
|
-
|
49
|
+
context 'when failed to list droplets' do
|
50
|
+
it 'raised with error' do
|
51
|
+
stub_droplets_fail
|
44
52
|
|
45
|
-
|
46
|
-
|
53
|
+
expect { snap_runner }
|
54
|
+
.to raise_error(DoSnapshot::DropletListError)
|
55
|
+
end
|
47
56
|
end
|
48
|
-
end
|
49
57
|
|
50
|
-
|
51
|
-
|
52
|
-
|
58
|
+
context 'when droplet failed for shutdown' do
|
59
|
+
it 'raised with error' do
|
60
|
+
stub_droplet_stop_fail(droplet_id)
|
53
61
|
|
54
|
-
|
55
|
-
|
62
|
+
expect { snap_runner }
|
63
|
+
.not_to raise_error
|
64
|
+
expect(DoSnapshot.logger.buffer)
|
65
|
+
.to include "Droplet id: #{droplet_id} is Failed to Power Off."
|
66
|
+
end
|
56
67
|
end
|
57
|
-
end
|
58
68
|
|
59
|
-
|
60
|
-
|
61
|
-
|
69
|
+
context 'when no snapshot created' do
|
70
|
+
it 'raised with error' do
|
71
|
+
stub_droplet_inactive(droplet_id)
|
72
|
+
stub_droplet_snapshot_fail(droplet_id, snapshot_name)
|
62
73
|
|
63
|
-
|
64
|
-
|
74
|
+
expect { snap_runner }
|
75
|
+
.to raise_error(DoSnapshot::SnapshotCreateError)
|
76
|
+
end
|
65
77
|
end
|
66
|
-
end
|
67
|
-
end
|
68
78
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
load_options
|
73
|
-
droplet = cmd.api.droplet droplet_id
|
74
|
-
expect { cmd.stop_droplet(droplet) }
|
75
|
-
.to raise_error(DoSnapshot::DropletShutdownError)
|
76
|
-
end
|
79
|
+
context 'when droplet not stopped' do
|
80
|
+
it 'skipped droplet' do
|
81
|
+
stub_droplet_stop_fail(droplet_id)
|
77
82
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
83
|
+
expect { snap_runner }
|
84
|
+
.not_to raise_error
|
85
|
+
expect(DoSnapshot.logger.buffer)
|
86
|
+
.to include "Droplet id: #{droplet_id} is Failed to Power Off."
|
87
|
+
end
|
88
|
+
end
|
84
89
|
end
|
85
|
-
end
|
86
90
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
91
|
+
describe '.stop_droplet' do
|
92
|
+
it 'when raised with error' do
|
93
|
+
stub_droplet_stop_fail(droplet_id)
|
94
|
+
load_options
|
95
|
+
droplet = cmd.api.droplet droplet_id
|
96
|
+
expect { cmd.stop_droplet(droplet) }
|
97
|
+
.not_to raise_error
|
98
|
+
expect(cmd.stop_droplet(droplet))
|
99
|
+
.to be_falsey
|
100
|
+
end
|
95
101
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
.
|
102
|
+
it 'when stopped' do
|
103
|
+
stub_droplet_inactive(droplet_id)
|
104
|
+
stub_droplet_stop(droplet_id)
|
105
|
+
load_options
|
106
|
+
droplet = cmd.api.droplet droplet_id
|
107
|
+
expect { cmd.stop_droplet(droplet) }
|
108
|
+
.not_to raise_error
|
109
|
+
expect(cmd.stop_droplet(droplet))
|
110
|
+
.to be_truthy
|
111
|
+
end
|
102
112
|
end
|
103
|
-
end
|
104
113
|
|
105
|
-
|
106
|
-
|
107
|
-
|
114
|
+
describe '.create_snapshot' do
|
115
|
+
it 'when raised with error' do
|
116
|
+
stub_droplet_inactive(droplet_id)
|
117
|
+
stub_droplet_snapshot_fail(droplet_id, snapshot_name)
|
118
|
+
load_options
|
119
|
+
droplet = cmd.api.droplet droplet_id
|
120
|
+
expect { cmd.create_snapshot(droplet) }
|
121
|
+
.to raise_error(DoSnapshot::SnapshotCreateError)
|
122
|
+
end
|
108
123
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
124
|
+
it 'when snapshot is created' do
|
125
|
+
stub_droplet_inactive(droplet_id)
|
126
|
+
stub_droplet_snapshot(droplet_id, snapshot_name)
|
127
|
+
load_options
|
128
|
+
droplet = cmd.api.droplet droplet_id
|
129
|
+
cmd.create_snapshot(droplet)
|
130
|
+
expect { cmd.create_snapshot(droplet) }
|
131
|
+
.not_to raise_error
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'when droplet is running' do
|
135
|
+
stub_droplet(droplet_id)
|
136
|
+
load_options
|
137
|
+
droplet = cmd.api.droplet droplet_id
|
138
|
+
cmd.create_snapshot(droplet)
|
139
|
+
expect { cmd.create_snapshot(droplet) }
|
140
|
+
.not_to raise_error
|
141
|
+
expect(DoSnapshot.logger.buffer)
|
142
|
+
.to include "Droplet id: #{droplet_id} must be Powered Off!"
|
143
|
+
end
|
113
144
|
end
|
114
145
|
|
115
|
-
|
116
|
-
|
146
|
+
describe '.fail_power_off' do
|
147
|
+
it 'when success' do
|
148
|
+
stub_droplet_inactive(droplet_id)
|
117
149
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
150
|
+
expect { cmd.fail_power_off(DoSnapshot::DropletShutdownError.new(droplet_id)) }
|
151
|
+
.not_to raise_error
|
152
|
+
expect(DoSnapshot.logger.buffer)
|
153
|
+
.to include 'Power On has been requested.'
|
154
|
+
end
|
155
|
+
|
156
|
+
it 'with request error' do
|
157
|
+
stub_droplet_fail(droplet_id)
|
158
|
+
|
159
|
+
expect { cmd.fail_power_off(DoSnapshot::DropletShutdownError.new(droplet_id)) }
|
160
|
+
.to raise_error(DoSnapshot::DropletFindError)
|
161
|
+
expect(DoSnapshot.logger.buffer)
|
162
|
+
.to include 'Droplet id: 100823 is Failed to Power Off.'
|
163
|
+
expect(DoSnapshot.logger.buffer)
|
164
|
+
.to include 'Droplet Not Found'
|
165
|
+
end
|
125
166
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
167
|
+
it 'with start error' do
|
168
|
+
stub_droplet_inactive(droplet_id)
|
169
|
+
stub_droplet_start_fail(droplet_id)
|
170
|
+
stub_event_fail(event_id)
|
130
171
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
172
|
+
expect { cmd.fail_power_off(DoSnapshot::DropletShutdownError.new(droplet_id)) }
|
173
|
+
.not_to raise_error
|
174
|
+
expect(DoSnapshot.logger.buffer)
|
175
|
+
.to include 'Power On failed to request.'
|
176
|
+
end
|
135
177
|
end
|
136
178
|
end
|
137
179
|
|
@@ -143,7 +185,7 @@ describe DoSnapshot::Command do
|
|
143
185
|
end
|
144
186
|
|
145
187
|
def load_options(options = nil)
|
146
|
-
options ||= default_options
|
188
|
+
options ||= default_options.merge(protocol: 1)
|
147
189
|
cmd.load_options(options, [:log, :mail, :smtp, :trace, :digital_ocean_client_id, :digital_ocean_api_key])
|
148
190
|
end
|
149
191
|
|
@@ -134,7 +134,7 @@ RSpec.describe DoSnapshot::Runner, type: :aruba do
|
|
134
134
|
hash_attribute_eq
|
135
135
|
|
136
136
|
expect(last_command).to have_exit_status(0)
|
137
|
-
expect(all_stdout).
|
137
|
+
expect(all_stdout).not_to include(t_sending_email)
|
138
138
|
end
|
139
139
|
|
140
140
|
it 'with smtp' do
|
data/spec/do_snapshot_spec.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# -*- encoding : utf-8 -*-
|
2
2
|
require 'spec_helper'
|
3
3
|
|
4
|
-
describe DoSnapshot do
|
4
|
+
RSpec.describe DoSnapshot do
|
5
5
|
include_context 'spec'
|
6
6
|
|
7
7
|
describe DoSnapshot::DropletFindError do
|
@@ -14,6 +14,26 @@ describe DoSnapshot do
|
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
+
describe DoSnapshot::DropletPowerError do
|
18
|
+
subject(:error) { described_class }
|
19
|
+
|
20
|
+
it 'should work' do
|
21
|
+
error.new(droplet_id)
|
22
|
+
expect(DoSnapshot.logger.buffer)
|
23
|
+
.to include "Droplet id: #{droplet_id} must be Powered Off!"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe DoSnapshot::EventError do
|
28
|
+
subject(:error) { described_class }
|
29
|
+
|
30
|
+
it 'should work' do
|
31
|
+
error.new(event_id)
|
32
|
+
expect(DoSnapshot.logger.buffer)
|
33
|
+
.to include "Event id: #{event_id} is failed!"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
17
37
|
describe DoSnapshot::DropletListError do
|
18
38
|
subject(:error) { described_class }
|
19
39
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: do_snapshot
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alexander Merkulov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-07-
|
11
|
+
date: 2015-07-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: '3.2'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: '3.2'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: digitalocean_c
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|