do_snapshot 0.0.14 → 0.0.15
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 +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
|