do_snapshot 0.0.14 → 0.0.15
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +29 -7
- data/lib/do_snapshot/adapter.rb +21 -0
- data/lib/do_snapshot/adapter/abstract.rb +44 -0
- data/lib/do_snapshot/adapter/digitalocean.rb +133 -0
- data/lib/do_snapshot/adapter/digitalocean_v2.rb +145 -0
- data/lib/do_snapshot/cli.rb +22 -12
- data/lib/do_snapshot/command.rb +7 -8
- data/lib/do_snapshot/version.rb +1 -1
- data/spec/do_snapshot/adapter/abstract_spec.rb +29 -0
- data/spec/do_snapshot/{api_spec.rb → adapter/digitalocean_spec.rb} +4 -3
- data/spec/do_snapshot/adapter/digitalocean_v2_spec.rb +223 -0
- data/spec/do_snapshot/cli_spec.rb +2 -1
- data/spec/do_snapshot/command_spec.rb +5 -8
- data/spec/fixtures/{error_message.json → digitalocean/v1/error_message.json} +0 -0
- data/spec/fixtures/{response_event.json → digitalocean/v1/response_event.json} +0 -0
- data/spec/fixtures/{show_droplet.json → digitalocean/v1/show_droplet.json} +0 -0
- data/spec/fixtures/{show_droplet_inactive.json → digitalocean/v1/show_droplet_inactive.json} +0 -0
- data/spec/fixtures/{show_droplets.json → digitalocean/v1/show_droplets.json} +0 -0
- data/spec/fixtures/{show_droplets_empty.json → digitalocean/v1/show_droplets_empty.json} +0 -0
- data/spec/fixtures/{show_event_done.json → digitalocean/v1/show_event_done.json} +0 -0
- data/spec/fixtures/{show_event_start.json → digitalocean/v1/show_event_start.json} +0 -0
- data/spec/fixtures/digitalocean/v2/empty.json +0 -0
- data/spec/fixtures/digitalocean/v2/error_message.json +4 -0
- data/spec/fixtures/digitalocean/v2/response_event.json +29 -0
- data/spec/fixtures/digitalocean/v2/show_droplet.json +102 -0
- data/spec/fixtures/digitalocean/v2/show_droplet_inactive.json +102 -0
- data/spec/fixtures/digitalocean/v2/show_droplets.json +284 -0
- data/spec/fixtures/digitalocean/v2/show_droplets_empty.json +5 -0
- data/spec/fixtures/digitalocean/v2/show_event_done.json +29 -0
- data/spec/fixtures/digitalocean/v2/show_event_power_off_done.json +29 -0
- data/spec/fixtures/digitalocean/v2/show_event_power_off_start.json +29 -0
- data/spec/fixtures/digitalocean/v2/show_event_power_on_done.json +29 -0
- data/spec/fixtures/digitalocean/v2/show_event_power_on_start.json +29 -0
- data/spec/fixtures/digitalocean/v2/show_event_start.json +29 -0
- data/spec/shared/api_helpers.rb +5 -80
- data/spec/shared/api_v1_helpers.rb +97 -0
- data/spec/shared/api_v2_helpers.rb +152 -0
- data/spec/shared/environment.rb +5 -14
- data/spec/shared/uri_helpers.rb +1 -0
- data/spec/spec_helper.rb +5 -3
- metadata +74 -23
- data/lib/do_snapshot/api.rb +0 -144
data/lib/do_snapshot/cli.rb
CHANGED
@@ -23,7 +23,7 @@ module DoSnapshot
|
|
23
23
|
set_mailer
|
24
24
|
|
25
25
|
# Check for keys via options
|
26
|
-
%w( digital_ocean_client_id digital_ocean_api_key ).each do |key|
|
26
|
+
%w( digital_ocean_client_id digital_ocean_api_key digital_ocean_access_token ).each do |key|
|
27
27
|
ENV[key.upcase] = options[key] if options.include? key
|
28
28
|
end
|
29
29
|
end
|
@@ -36,6 +36,14 @@ module DoSnapshot
|
|
36
36
|
|
37
37
|
### Examples
|
38
38
|
|
39
|
+
Select api version (1, 2):
|
40
|
+
|
41
|
+
$ do_snapshot -a 2
|
42
|
+
|
43
|
+
Set DigitalOcean keys:
|
44
|
+
|
45
|
+
$ do_snapshot --digital_ocean_api_token SOMELONGTOKEN
|
46
|
+
|
39
47
|
Keep latest 5 and cleanup older if maximum is reached, verbose:
|
40
48
|
|
41
49
|
$ do_snapshot -k 5 -c -v
|
@@ -64,6 +72,12 @@ module DoSnapshot
|
|
64
72
|
|
65
73
|
VERSION: #{DoSnapshot::VERSION}
|
66
74
|
LONGDESC
|
75
|
+
method_option :protocol,
|
76
|
+
type: :numeric,
|
77
|
+
default: 1,
|
78
|
+
aliases: %w( -p ),
|
79
|
+
banner: '1',
|
80
|
+
desc: 'Select api version.'
|
67
81
|
method_option :only,
|
68
82
|
type: :array,
|
69
83
|
default: [],
|
@@ -90,7 +104,7 @@ module DoSnapshot
|
|
90
104
|
desc: 'Delay between snapshot operation status requests.'
|
91
105
|
method_option :timeout,
|
92
106
|
type: :numeric,
|
93
|
-
default:
|
107
|
+
default: 3600,
|
94
108
|
banner: '250',
|
95
109
|
desc: 'Timeout in sec\'s for events like Power Off or Create Snapshot.'
|
96
110
|
method_option :mail,
|
@@ -109,6 +123,7 @@ module DoSnapshot
|
|
109
123
|
banner: '/Users/someone/.do_snapshot/main.log',
|
110
124
|
desc: 'Log file path. By default logging is disabled.'
|
111
125
|
method_option :clean,
|
126
|
+
default: true,
|
112
127
|
type: :boolean,
|
113
128
|
aliases: %w( -c ),
|
114
129
|
desc: 'Cleanup snapshots after create. If you have more images than you want to `keep`, older will be deleted.'
|
@@ -125,6 +140,10 @@ module DoSnapshot
|
|
125
140
|
aliases: %w( -q ),
|
126
141
|
desc: 'Quiet mode. If don\'t need any messages and in console.'
|
127
142
|
|
143
|
+
method_option :digital_ocean_access_token,
|
144
|
+
type: :string,
|
145
|
+
banner: 'YOURLONGAPITOKEN',
|
146
|
+
desc: 'DIGITAL_OCEAN_ACCESS_TOKEN. if you can\'t use environment.'
|
128
147
|
method_option :digital_ocean_client_id,
|
129
148
|
type: :string,
|
130
149
|
banner: 'YOURLONGAPICLIENTID',
|
@@ -135,8 +154,7 @@ module DoSnapshot
|
|
135
154
|
desc: 'DIGITAL_OCEAN_API_KEY. if you can\'t use environment.'
|
136
155
|
|
137
156
|
def snap
|
138
|
-
|
139
|
-
Command.load_options options, %w( log mail smtp trace digital_ocean_client_id digital_ocean_api_key )
|
157
|
+
Command.load_options options, %w( log mail smtp trace digital_ocean_client_id digital_ocean_api_key digital_ocean_access_token )
|
140
158
|
Command.snap
|
141
159
|
rescue => e
|
142
160
|
Command.fail_power_off(e) if [SnapshotCreateError, DropletShutdownError].include?(e.class)
|
@@ -182,14 +200,6 @@ module DoSnapshot
|
|
182
200
|
Log.error t
|
183
201
|
end
|
184
202
|
end
|
185
|
-
|
186
|
-
# Check for DigitalOcean API keys
|
187
|
-
def try_keys_first
|
188
|
-
Log.debug 'Checking DigitalOcean Id\'s.'
|
189
|
-
%w( DIGITAL_OCEAN_CLIENT_ID DIGITAL_OCEAN_API_KEY ).each do |key|
|
190
|
-
Log.error "You must have #{key} in environment or set it via options." if ENV[key].blank?
|
191
|
-
end
|
192
|
-
end
|
193
203
|
end
|
194
204
|
end
|
195
205
|
end
|
data/lib/do_snapshot/command.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# -*- encoding : utf-8 -*-
|
2
|
-
require_relative '
|
2
|
+
require_relative 'adapter'
|
3
3
|
|
4
4
|
module DoSnapshot
|
5
5
|
# Our commands live here :)
|
@@ -40,7 +40,7 @@ module DoSnapshot
|
|
40
40
|
today = DateTime.now
|
41
41
|
name = "#{droplet.name}_#{today.strftime('%Y_%m_%d')}"
|
42
42
|
# noinspection RubyResolve
|
43
|
-
snapshot_size =
|
43
|
+
snapshot_size = api.snapshots(droplet).size
|
44
44
|
|
45
45
|
Log.debug 'Wait until snapshot will be created.'
|
46
46
|
|
@@ -63,13 +63,13 @@ module DoSnapshot
|
|
63
63
|
end
|
64
64
|
|
65
65
|
def api
|
66
|
-
@api ||=
|
66
|
+
@api ||= DoSnapshot::Adapter.api(protocol, delay: delay, timeout: timeout)
|
67
67
|
end
|
68
68
|
|
69
69
|
protected
|
70
70
|
|
71
71
|
attr_accessor :droplets, :exclude, :only
|
72
|
-
attr_accessor :keep, :quiet, :stop, :clean, :timeout, :delay
|
72
|
+
attr_accessor :keep, :quiet, :stop, :clean, :timeout, :delay, :protocol
|
73
73
|
|
74
74
|
attr_writer :notify, :threads, :api
|
75
75
|
|
@@ -95,7 +95,7 @@ module DoSnapshot
|
|
95
95
|
#
|
96
96
|
def load_droplets
|
97
97
|
Log.debug 'Loading list of DigitalOcean droplets'
|
98
|
-
self.droplets = api.droplets
|
98
|
+
self.droplets = api.droplets
|
99
99
|
end
|
100
100
|
|
101
101
|
# Dispatch received droplets, each by each.
|
@@ -130,8 +130,7 @@ module DoSnapshot
|
|
130
130
|
#
|
131
131
|
def prepare_droplet(id, name)
|
132
132
|
Log.debug "Droplet id: #{id} name: #{name} "
|
133
|
-
|
134
|
-
droplet = instance.droplet
|
133
|
+
droplet = api.droplet id
|
135
134
|
|
136
135
|
return unless droplet
|
137
136
|
Log.info "Preparing droplet id: #{droplet.id} name: #{droplet.name} to take snapshot."
|
@@ -141,7 +140,7 @@ module DoSnapshot
|
|
141
140
|
|
142
141
|
def too_much_snapshots(instance)
|
143
142
|
# noinspection RubyResolve
|
144
|
-
return false unless
|
143
|
+
return false unless api.snapshots(instance).size >= keep
|
145
144
|
warning_size(instance.id, instance.name, keep)
|
146
145
|
stop ? true : false
|
147
146
|
end
|
data/lib/do_snapshot/version.rb
CHANGED
@@ -0,0 +1,29 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe DoSnapshot::Adapter::Abstract do
|
5
|
+
include_context 'spec'
|
6
|
+
|
7
|
+
subject(:api) { described_class }
|
8
|
+
subject(:log) { DoSnapshot::Log }
|
9
|
+
|
10
|
+
describe '.initialize' do
|
11
|
+
describe '#delay' do
|
12
|
+
let(:delay) { 5 }
|
13
|
+
let(:instance) { api.new(delay: delay) }
|
14
|
+
it('with custom delay') { expect(instance.delay).to eq delay }
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#timeout' do
|
18
|
+
let(:timeout) { 5 }
|
19
|
+
let(:instance) { api.new(timeout: timeout) }
|
20
|
+
it('with custom timeout') { expect(instance.timeout).to eq timeout }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
before(:each) do
|
25
|
+
log.buffer = %w()
|
26
|
+
log.verbose = false
|
27
|
+
log.quiet = true
|
28
|
+
end
|
29
|
+
end
|
@@ -1,8 +1,9 @@
|
|
1
1
|
# -*- encoding : utf-8 -*-
|
2
2
|
require 'spec_helper'
|
3
3
|
|
4
|
-
describe DoSnapshot::
|
4
|
+
describe DoSnapshot::Adapter::Digitalocean do
|
5
5
|
include_context 'spec'
|
6
|
+
include_context 'api_v1_helpers'
|
6
7
|
|
7
8
|
subject(:api) { described_class }
|
8
9
|
subject(:log) { DoSnapshot::Log }
|
@@ -178,7 +179,7 @@ describe DoSnapshot::API do
|
|
178
179
|
stub_image_destroy(image_id2)
|
179
180
|
|
180
181
|
droplet = instance.droplet(droplet_id)
|
181
|
-
expect { instance.cleanup_snapshots(droplet
|
182
|
+
expect { instance.cleanup_snapshots(droplet, 1) }
|
182
183
|
.not_to raise_error
|
183
184
|
expect(log.buffer)
|
184
185
|
.to include 'Snapshot name: mrcr.ru_2014_07_19 delete requested.'
|
@@ -197,7 +198,7 @@ describe DoSnapshot::API do
|
|
197
198
|
stub_image_destroy_fail(image_id2)
|
198
199
|
|
199
200
|
droplet = instance.droplet(droplet_id)
|
200
|
-
expect { instance.cleanup_snapshots(droplet
|
201
|
+
expect { instance.cleanup_snapshots(droplet, 1) }
|
201
202
|
.not_to raise_error
|
202
203
|
expect(log.buffer)
|
203
204
|
.to include 'Some Message'
|
@@ -0,0 +1,223 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe DoSnapshot::Adapter::DigitaloceanV2 do
|
5
|
+
include_context 'spec'
|
6
|
+
include_context 'api_v2_helpers'
|
7
|
+
|
8
|
+
subject(:api) { described_class }
|
9
|
+
subject(:log) { DoSnapshot::Log }
|
10
|
+
|
11
|
+
let(:event_id) { '7499' }
|
12
|
+
|
13
|
+
describe '.initialize' do
|
14
|
+
describe '#delay' do
|
15
|
+
let(:delay) { 5 }
|
16
|
+
let(:instance) { api.new(delay: delay) }
|
17
|
+
it('with custom delay') { expect(instance.delay).to eq delay }
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '#timeout' do
|
21
|
+
let(:timeout) { 5 }
|
22
|
+
let(:instance) { api.new(timeout: timeout) }
|
23
|
+
it('with custom timeout') { expect(instance.timeout).to eq timeout }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe 'droplets' do
|
28
|
+
let(:instance) { api.new(delay: delay, timeout: timeout) }
|
29
|
+
include_context 'uri_helpers'
|
30
|
+
|
31
|
+
describe '.droplet' do
|
32
|
+
it 'with droplet' do
|
33
|
+
stub_droplet(droplet_id)
|
34
|
+
|
35
|
+
instance.droplet(droplet_id)
|
36
|
+
|
37
|
+
expect(a_request(:get, droplet_url))
|
38
|
+
.to have_been_made
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'with error' do
|
42
|
+
stub_droplet_fail(droplet_id)
|
43
|
+
|
44
|
+
expect { instance.droplet(droplet_id) }
|
45
|
+
.to raise_error
|
46
|
+
expect(log.buffer)
|
47
|
+
.to include 'Droplet Not Found'
|
48
|
+
|
49
|
+
expect(a_request(:get, droplet_url))
|
50
|
+
.to have_been_made
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe '.droplets' do
|
55
|
+
it 'with droplets' do
|
56
|
+
stub_droplets
|
57
|
+
|
58
|
+
instance.droplets
|
59
|
+
|
60
|
+
expect(a_request(:get, droplets_uri))
|
61
|
+
.to have_been_made
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'with error' do
|
65
|
+
stub_droplets_fail
|
66
|
+
|
67
|
+
expect { instance.droplets }.to raise_error
|
68
|
+
expect(log.buffer)
|
69
|
+
.to include 'Droplet Listing is failed to retrieve'
|
70
|
+
|
71
|
+
expect(a_request(:get, droplets_uri))
|
72
|
+
.to have_been_made
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe '.start_droplet' do
|
77
|
+
it 'with event' do
|
78
|
+
stub_droplet_inactive(droplet_id)
|
79
|
+
stub_droplet_start(droplet_id)
|
80
|
+
|
81
|
+
instance.start_droplet(droplet_id)
|
82
|
+
expect(log.buffer).to include 'Power On has been requested.'
|
83
|
+
|
84
|
+
expect(a_request(:post, droplet_start_url))
|
85
|
+
.to have_been_made
|
86
|
+
expect(a_request(:get, droplet_url))
|
87
|
+
.to have_been_made
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'with warning message' do
|
91
|
+
stub_droplet(droplet_id)
|
92
|
+
|
93
|
+
expect { instance.start_droplet(droplet_id) }
|
94
|
+
.not_to raise_error
|
95
|
+
expect(log.buffer)
|
96
|
+
.to include 'Droplet is still running.'
|
97
|
+
|
98
|
+
expect(a_request(:get, droplet_url))
|
99
|
+
.to have_been_made
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'with error' do
|
103
|
+
stub_droplet_fail(droplet_id)
|
104
|
+
|
105
|
+
expect { instance.start_droplet(droplet_id) }
|
106
|
+
.to raise_error
|
107
|
+
|
108
|
+
expect(a_request(:get, droplet_url))
|
109
|
+
.to have_been_made
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe '.stop_droplet' do
|
114
|
+
it 'with event' do
|
115
|
+
stub_event_done(event_id)
|
116
|
+
stub_droplet_stop(droplet_id)
|
117
|
+
|
118
|
+
instance.stop_droplet(droplet_id)
|
119
|
+
|
120
|
+
expect(a_request(:post, droplet_stop_url))
|
121
|
+
.to have_been_made
|
122
|
+
expect(a_request(:get, action_find_url))
|
123
|
+
.to have_been_made
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'with error' do
|
127
|
+
stub_droplet_stop_fail(droplet_id)
|
128
|
+
|
129
|
+
expect { instance.stop_droplet(droplet_id) }
|
130
|
+
.to raise_error
|
131
|
+
expect(log.buffer)
|
132
|
+
.to include 'Droplet id: 100823 is Failed to Power Off.'
|
133
|
+
|
134
|
+
expect(a_request(:post, droplet_stop_url))
|
135
|
+
.to have_been_made
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
describe '.create_snapshot' do
|
140
|
+
it 'with success' do
|
141
|
+
stub_event_done(event_id)
|
142
|
+
stub_droplet_snapshot(droplet_id, snapshot_name)
|
143
|
+
|
144
|
+
expect { instance.create_snapshot(droplet_id, snapshot_name) }
|
145
|
+
.not_to raise_error
|
146
|
+
|
147
|
+
expect(a_request(:post, droplet_snapshot_url))
|
148
|
+
.to have_been_made
|
149
|
+
expect(a_request(:get, action_find_url))
|
150
|
+
.to have_been_made
|
151
|
+
end
|
152
|
+
|
153
|
+
it 'with error' do
|
154
|
+
stub_droplet_snapshot_fail(droplet_id, snapshot_name)
|
155
|
+
|
156
|
+
expect { instance.create_snapshot(droplet_id, snapshot_name) }
|
157
|
+
.to raise_error
|
158
|
+
|
159
|
+
expect(a_request(:post, droplet_snapshot_url))
|
160
|
+
.to have_been_made
|
161
|
+
end
|
162
|
+
|
163
|
+
it 'with event error' do
|
164
|
+
stub_droplet_snapshot(droplet_id, snapshot_name)
|
165
|
+
stub_event_fail(event_id)
|
166
|
+
|
167
|
+
expect { instance.create_snapshot(droplet_id, snapshot_name) }
|
168
|
+
.to raise_error
|
169
|
+
|
170
|
+
expect(a_request(:post, droplet_snapshot_url))
|
171
|
+
.to have_been_made
|
172
|
+
expect(a_request(:get, action_find_url))
|
173
|
+
.to have_been_made
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
describe '.cleanup_snapshots' do
|
178
|
+
it 'with success' do
|
179
|
+
stub_droplet(droplet_id)
|
180
|
+
stub_image_destroy(image_id)
|
181
|
+
stub_image_destroy(image_id2)
|
182
|
+
|
183
|
+
droplet = instance.droplet(droplet_id)
|
184
|
+
expect { instance.cleanup_snapshots(droplet, 1) }
|
185
|
+
.not_to raise_error
|
186
|
+
expect(log.buffer)
|
187
|
+
.to include 'Snapshot: 5019770 delete requested.'
|
188
|
+
|
189
|
+
expect(a_request(:get, droplet_url))
|
190
|
+
.to have_been_made
|
191
|
+
expect(a_request(:delete, image_destroy_url))
|
192
|
+
.to have_been_made
|
193
|
+
expect(a_request(:delete, image_destroy2_url))
|
194
|
+
.to have_been_made
|
195
|
+
end
|
196
|
+
|
197
|
+
it 'with warning message' do
|
198
|
+
stub_droplet(droplet_id)
|
199
|
+
stub_image_destroy_fail(image_id)
|
200
|
+
stub_image_destroy_fail(image_id2)
|
201
|
+
|
202
|
+
droplet = instance.droplet(droplet_id)
|
203
|
+
expect { instance.cleanup_snapshots(droplet, 1) }
|
204
|
+
.not_to raise_error
|
205
|
+
expect(log.buffer)
|
206
|
+
.to include 'Destroy of snapshot 5019903 for droplet id: 100823 name: example.com is failed.'
|
207
|
+
|
208
|
+
expect(a_request(:get, droplet_url))
|
209
|
+
.to have_been_made
|
210
|
+
expect(a_request(:delete, image_destroy_url))
|
211
|
+
.to have_been_made
|
212
|
+
expect(a_request(:delete, image_destroy2_url))
|
213
|
+
.to have_been_made
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
before(:each) do
|
219
|
+
log.buffer = %w()
|
220
|
+
log.verbose = false
|
221
|
+
log.quiet = true
|
222
|
+
end
|
223
|
+
end
|
@@ -3,10 +3,11 @@ require 'spec_helper'
|
|
3
3
|
|
4
4
|
describe DoSnapshot::CLI do
|
5
5
|
include_context 'spec'
|
6
|
+
include_context 'api_v1_helpers'
|
6
7
|
|
7
8
|
subject(:cli) { described_class }
|
8
9
|
subject(:command) { DoSnapshot::Command }
|
9
|
-
subject(:api) { DoSnapshot::
|
10
|
+
subject(:api) { DoSnapshot::Adapter::Digitalocean }
|
10
11
|
|
11
12
|
describe '.initialize' do
|
12
13
|
it 'with args & options' do
|