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.
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