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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +29 -7
  3. data/lib/do_snapshot/adapter.rb +21 -0
  4. data/lib/do_snapshot/adapter/abstract.rb +44 -0
  5. data/lib/do_snapshot/adapter/digitalocean.rb +133 -0
  6. data/lib/do_snapshot/adapter/digitalocean_v2.rb +145 -0
  7. data/lib/do_snapshot/cli.rb +22 -12
  8. data/lib/do_snapshot/command.rb +7 -8
  9. data/lib/do_snapshot/version.rb +1 -1
  10. data/spec/do_snapshot/adapter/abstract_spec.rb +29 -0
  11. data/spec/do_snapshot/{api_spec.rb → adapter/digitalocean_spec.rb} +4 -3
  12. data/spec/do_snapshot/adapter/digitalocean_v2_spec.rb +223 -0
  13. data/spec/do_snapshot/cli_spec.rb +2 -1
  14. data/spec/do_snapshot/command_spec.rb +5 -8
  15. data/spec/fixtures/{error_message.json → digitalocean/v1/error_message.json} +0 -0
  16. data/spec/fixtures/{response_event.json → digitalocean/v1/response_event.json} +0 -0
  17. data/spec/fixtures/{show_droplet.json → digitalocean/v1/show_droplet.json} +0 -0
  18. data/spec/fixtures/{show_droplet_inactive.json → digitalocean/v1/show_droplet_inactive.json} +0 -0
  19. data/spec/fixtures/{show_droplets.json → digitalocean/v1/show_droplets.json} +0 -0
  20. data/spec/fixtures/{show_droplets_empty.json → digitalocean/v1/show_droplets_empty.json} +0 -0
  21. data/spec/fixtures/{show_event_done.json → digitalocean/v1/show_event_done.json} +0 -0
  22. data/spec/fixtures/{show_event_start.json → digitalocean/v1/show_event_start.json} +0 -0
  23. data/spec/fixtures/digitalocean/v2/empty.json +0 -0
  24. data/spec/fixtures/digitalocean/v2/error_message.json +4 -0
  25. data/spec/fixtures/digitalocean/v2/response_event.json +29 -0
  26. data/spec/fixtures/digitalocean/v2/show_droplet.json +102 -0
  27. data/spec/fixtures/digitalocean/v2/show_droplet_inactive.json +102 -0
  28. data/spec/fixtures/digitalocean/v2/show_droplets.json +284 -0
  29. data/spec/fixtures/digitalocean/v2/show_droplets_empty.json +5 -0
  30. data/spec/fixtures/digitalocean/v2/show_event_done.json +29 -0
  31. data/spec/fixtures/digitalocean/v2/show_event_power_off_done.json +29 -0
  32. data/spec/fixtures/digitalocean/v2/show_event_power_off_start.json +29 -0
  33. data/spec/fixtures/digitalocean/v2/show_event_power_on_done.json +29 -0
  34. data/spec/fixtures/digitalocean/v2/show_event_power_on_start.json +29 -0
  35. data/spec/fixtures/digitalocean/v2/show_event_start.json +29 -0
  36. data/spec/shared/api_helpers.rb +5 -80
  37. data/spec/shared/api_v1_helpers.rb +97 -0
  38. data/spec/shared/api_v2_helpers.rb +152 -0
  39. data/spec/shared/environment.rb +5 -14
  40. data/spec/shared/uri_helpers.rb +1 -0
  41. data/spec/spec_helper.rb +5 -3
  42. metadata +74 -23
  43. data/lib/do_snapshot/api.rb +0 -144
@@ -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: 600,
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
- try_keys_first
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
@@ -1,5 +1,5 @@
1
1
  # -*- encoding : utf-8 -*-
2
- require_relative 'api'
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 = droplet.snapshots.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 ||= API.new(delay: delay, timeout: timeout)
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.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
- instance = api.droplet id
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 instance.snapshots.size >= keep
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
@@ -2,5 +2,5 @@
2
2
  # Current version
3
3
  #
4
4
  module DoSnapshot
5
- VERSION = '0.0.14'
5
+ VERSION = '0.0.15'
6
6
  end
@@ -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::API do
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.droplet, 1) }
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.droplet, 1) }
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::API }
10
+ subject(:api) { DoSnapshot::Adapter::Digitalocean }
10
11
 
11
12
  describe '.initialize' do
12
13
  it 'with args & options' do