do_snapshot 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +20 -32
  3. data/lib/do_snapshot.rb +4 -0
  4. data/lib/do_snapshot/adapter.rb +22 -12
  5. data/lib/do_snapshot/command.rb +3 -4
  6. data/lib/do_snapshot/mail.rb +4 -2
  7. data/lib/do_snapshot/version.rb +1 -1
  8. data/spec/do_snapshot/adapter/abstract_spec.rb +1 -1
  9. data/spec/do_snapshot/adapter/digitalocean_v2_spec.rb +1 -1
  10. data/spec/do_snapshot/adapter_spec.rb +36 -0
  11. data/spec/do_snapshot/cli_spec.rb +2 -2
  12. data/spec/do_snapshot/command_spec.rb +1 -5
  13. data/spec/do_snapshot/log_spec.rb +12 -30
  14. data/spec/do_snapshot/mail_spec.rb +1 -1
  15. data/spec/do_snapshot/runner_spec.rb +1 -30
  16. data/spec/do_snapshot_spec.rb +1 -1
  17. data/spec/shared/api_helpers.rb +1 -1
  18. data/spec/shared/api_v2_helpers.rb +1 -1
  19. data/spec/shared/environment.rb +1 -1
  20. data/spec/shared/uri_helpers.rb +1 -1
  21. data/spec/spec_helper.rb +3 -1
  22. metadata +4 -23
  23. data/lib/do_snapshot/adapter/digitalocean.rb +0 -123
  24. data/spec/do_snapshot/adapter/digitalocean_spec.rb +0 -265
  25. data/spec/fixtures/digitalocean/v1/error_message.json +0 -4
  26. data/spec/fixtures/digitalocean/v1/response_event.json +0 -4
  27. data/spec/fixtures/digitalocean/v1/show_droplet.json +0 -39
  28. data/spec/fixtures/digitalocean/v1/show_droplet_inactive.json +0 -39
  29. data/spec/fixtures/digitalocean/v1/show_droplets.json +0 -35
  30. data/spec/fixtures/digitalocean/v1/show_droplets_empty.json +0 -4
  31. data/spec/fixtures/digitalocean/v1/show_event_done.json +0 -10
  32. data/spec/fixtures/digitalocean/v1/show_event_start.json +0 -10
  33. data/spec/shared/api_v1_helpers.rb +0 -97
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 77cc4c07beb253d0aa7fbe1870fb45f014c6f339
4
- data.tar.gz: 0c2acaaaddadd3626423894aaf25c8fc3a84dfb1
3
+ metadata.gz: 7c7c9e3cd13d683e5ba91602e678fdcdb83cc434
4
+ data.tar.gz: e3dc35838e951eed9fdd6390203573af0de761a9
5
5
  SHA512:
6
- metadata.gz: a15514139088299e98b567a227205cc14f6d3e71b7a19707bf1ad9520690962104b57dc3f8b80bb3bae9fa515d511b082f96adfd94ab4d8909d17a846c265369
7
- data.tar.gz: 33e4c117829a0ede9809c7930426811e8110ac9fb3d8b6ee35894e0f622535cf285205ccd263d7fd66d17ee6efc82cffeecebac2805d95e292752e8bbed0ec42
6
+ metadata.gz: bd1fd908c738b06b4ebb86d11f8e05f55c343d21eef81d769e103603d2e19f9462553ca9a7d0256df2d0ae0ce05fbd758ef3d20032aa277b35f891f3a9f512d2
7
+ data.tar.gz: e49489aaab514f945a3eb7f345c3c351545c3e099bbca988202796ebea2172cc32596b8472b87dfe201269892de7faaf6d2903adfc13de09beeb225f13559b84
data/README.md CHANGED
@@ -12,18 +12,20 @@
12
12
 
13
13
  Use this tool to backup DigitalOcean droplet's via snapshot method, on the fly!
14
14
 
15
- ## Breaking changes: now we use DO API V2 by default, due V1 deprecation at 11.2015.
15
+ ## API Changes:
16
+ - 08.01.16: now we have to use DO API V2 only, because V1 is not work anymore.
17
+ - 17.10.15: now we use DO API V2 by default, due V1 deprecation at 11.2015.
16
18
 
17
- Here some features:
19
+ Here are some features:
18
20
 
19
- - Multiple threads out of the box. No matter how much droplet's you have.
21
+ - Multiple threads out of the box, no matter how many droplets you have.
20
22
  - Snapshots Auto-Cleanup.
21
- - Auto-Boot Droplet back if Snapshot Event is failed or bad connection exception.
22
- - Binary special for cron and command-line. Homebrew, Standalone installers.
23
- - Mail notifications when fail or maximum of snapshots is reached for one or multiple droplets.
23
+ - Auto-Boot Droplet if Snapshot Event fails or encounters a bad connection exception.
24
+ - Special binaries for cron and command-line, Homebrew, and standalone installers.
25
+ - Mail notifications when a snapshot fails or the maximum number of snapshots is reached for a droplet or droplets.
24
26
  - Custom mail settings (You can set [Pony](https://github.com/benprew/pony) mail settings).
25
- - Stop mode (when you don't want to create new snapshots when maximum is reached).
26
- - Timeout option for long requests or uncaught loops. By default it 600 seconds, but you can change it by hand.
27
+ - Stop mode (automatically stop creating new snapshots when the maximum is reached).
28
+ - Timeout option for long requests or uncaught loops. Defaults to 600 seconds, but can be changed.
27
29
  - Logging into selected directory.
28
30
  - Verbose mode for research.
29
31
  - Quiet mode for silence.
@@ -34,16 +36,6 @@ Ruby versions 1.9.3 and higher. JRuby 1.7, 9.0.0.0 or later is also supported.
34
36
 
35
37
  <img src="https://raw.githubusercontent.com/merqlove/do_snapshot/master/assets/example.png" style="max-width:100%" alt="DoSnaphot example">
36
38
 
37
- ### You can ask me, "Why you made this tool?"
38
-
39
- - First. I needed stable tool, which can provide for me automatic Snapshot feature for all of my Droplets via Cron planner.
40
- - I don't want to think how much snapshots for each droplet i have.
41
- - I don't wont to sleep when my droplets Offline!!! And i wanted tool which can BOOT back droplets, which failed to snapshot.
42
- - Also i want to understand what's going on if there some error. Mail is my choice. But logs also good.
43
- - And ... sure ;) We want to do it fast as rocket! :)
44
- - more more more...
45
- - So this tool can save a lot of time for people.
46
-
47
39
  ## Installation
48
40
 
49
41
  Install it yourself as:
@@ -91,26 +83,12 @@ You'll need to generate an access token in Digital Ocean's control panel at http
91
83
  If you want to set keys without environment, than set it via options when you run do_snapshot:
92
84
 
93
85
  $ do_snapshot --digital-ocean-access-token YOURLONGTOKEN
94
-
95
- ### Digitalocean API V1:
96
- You'll need to generate an access token in Digital Ocean's control panel at https://cloud.digitalocean.com/api_access
97
-
98
- $ export DIGITAL_OCEAN_CLIENT_ID="SOMEID"
99
- $ export DIGITAL_OCEAN_API_KEY="SOMEKEY"
100
-
101
- If you want to set keys without environment, than set it via options when you run do_snapshot:
102
-
103
- $ do_snapshot --digital-ocean-client-id YOURLONGAPICLIENTID --digital-ocean-api-key YOURLONGAPIKEY
104
86
 
105
87
  ### How-To (Here is also [Longren Tutorial](https://longren.io/automate-making-snapshots-of-your-digitalocean-droplets/))
106
88
 
107
89
  Here we `keeping` only 5 **latest** snapshots and cleanup older after new one is created. If creation of snapshots failed no one will be deleted. By default we keeping `10` droplets.
108
90
 
109
91
  $ do_snapshot --keep 5 -c
110
-
111
- Using API V1:
112
-
113
- $ do_snapshot -p 1
114
92
 
115
93
  Keep latest 3 from selected droplet:
116
94
 
@@ -190,6 +168,16 @@ For working mailer you need to set e-mail settings via run options.
190
168
 
191
169
  You can optionally specify parameters to select or exclude some droplets.
192
170
 
171
+ ## You can ask, "Why you made this tool?"
172
+
173
+ - First. I needed stable tool, which can provide for me automatic Snapshot feature for all of my Droplets via Cron planner.
174
+ - I don't want to think how much snapshots for each droplet i have.
175
+ - I don't wont to sleep when my droplets Offline!!! And i wanted tool which can BOOT back droplets, which failed to snapshot.
176
+ - Also i want to understand what's going on if there some error. Mail is my choice. But logs also good.
177
+ - And ... sure ;) We want to do it fast as rocket! :)
178
+ - more more more...
179
+ - So this tool can save a lot of time for people.
180
+
193
181
  ## Donating:
194
182
  Support this project and others by [merqlove](https://gratipay.com/~merqlove/) via [gratipay](https://gratipay.com/~merqlove/).
195
183
  [![Support via Gratipay](https://cdn.rawgit.com/gratipay/gratipay-badge/2.3.0/dist/gratipay.png)](https://gratipay.com/merqlove/)
@@ -41,6 +41,10 @@ module DoSnapshot
41
41
  #
42
42
  class NoTokenError < StandardError; end
43
43
 
44
+ # Protocol must exist.
45
+ #
46
+ class NoProtocolError < StandardError; end
47
+
44
48
  # Base Exception for cases when we need id for log and/or something actions.
45
49
  #
46
50
  class RequestActionError < RequestError
@@ -1,21 +1,31 @@
1
- require_relative 'adapter/abstract'
2
-
3
- require_relative 'adapter/digitalocean'
4
- require_relative 'adapter/digitalocean_v2'
5
-
6
1
  module DoSnapshot
7
2
  # Adapter interface for API connections
8
3
  # Ability to select DigitalOcean API versions.
9
4
  #
10
5
  module Adapter
11
- def api(protocol, options)
12
- case protocol
13
- when 1
14
- return Digitalocean.new(options)
15
- else
16
- return DigitaloceanV2.new(options)
6
+ autoload :Abstract, 'do_snapshot/adapter/abstract'
7
+ autoload :DigitaloceanV2, 'do_snapshot/adapter/digitalocean_v2'
8
+
9
+ class << self
10
+ def api(protocol, options = {})
11
+ konst = find_protocol(protocol)
12
+ fail DoSnapshot::NoProtocolError, "Not existing protocol: #{protocol}." unless
13
+ DoSnapshot::Adapter.const_defined?(konst)
14
+ obj = DoSnapshot::Adapter.const_get(konst)
15
+ obj.new(options)
16
+ end
17
+
18
+ private
19
+
20
+ def find_protocol(protocol)
21
+ if protocol.is_a?(Integer)
22
+ "DigitaloceanV#{protocol}"
23
+ elsif protocol.is_a?(String)
24
+ protocol
25
+ else
26
+ 'DigitaloceanV2'
27
+ end
17
28
  end
18
29
  end
19
- module_function :api
20
30
  end
21
31
  end
@@ -172,14 +172,13 @@ module DoSnapshot
172
172
 
173
173
  return unless droplet
174
174
  logger.info "Preparing droplet id: #{droplet.id} name: #{droplet.name} to take snapshot."
175
- return if too_much_snapshots(droplet)
175
+ return if too_much_snapshots?(droplet)
176
176
  processed_droplet_ids << droplet.id
177
177
  thread_runner(droplet)
178
178
  end
179
179
 
180
- def too_much_snapshots(instance)
181
- # noinspection RubyResolve
182
- return false unless api.snapshots(instance).size >= keep
180
+ def too_much_snapshots?(instance)
181
+ return false if api.snapshots(instance).size < keep
183
182
  warning_size(instance.id, instance.name, keep)
184
183
  stop ? true : false
185
184
  end
@@ -8,8 +8,6 @@ module DoSnapshot
8
8
  # Shared mailer.
9
9
  #
10
10
  class Mail
11
- include DoSnapshot::Helpers
12
-
13
11
  attr_writer :mailer, :opts_default, :smtp_default
14
12
 
15
13
  def initialize(options = {})
@@ -58,6 +56,10 @@ module DoSnapshot
58
56
 
59
57
  protected
60
58
 
59
+ def logger
60
+ DoSnapshot::Helpers::UniversalLogger
61
+ end
62
+
61
63
  def opts_default
62
64
  @opts_default ||= {
63
65
  subject: 'Digital Ocean: maximum snapshots is reached.',
@@ -2,5 +2,5 @@
2
2
  # Current version
3
3
  #
4
4
  module DoSnapshot
5
- VERSION = '0.5.0'
5
+ VERSION = '0.6.0'
6
6
  end
@@ -2,7 +2,7 @@
2
2
  require 'spec_helper'
3
3
 
4
4
  RSpec.describe DoSnapshot::Adapter::Abstract do
5
- include_context 'spec'
5
+ include_context 'environment'
6
6
 
7
7
  subject(:api) { described_class }
8
8
 
@@ -2,7 +2,7 @@
2
2
  require 'spec_helper'
3
3
 
4
4
  RSpec.describe DoSnapshot::Adapter::DigitaloceanV2 do
5
- include_context 'spec'
5
+ include_context 'environment'
6
6
  include_context 'api_v2_helpers'
7
7
 
8
8
  subject(:api) { described_class }
@@ -0,0 +1,36 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'spec_helper'
3
+
4
+ RSpec.describe DoSnapshot::Adapter do
5
+ include_context 'environment'
6
+
7
+ module DoSnapshot
8
+ module Adapter
9
+ class CustomAdapter # rubocop:disable Style/Documentation
10
+ def initialize(_ = {}); end
11
+ end
12
+ end
13
+ end
14
+
15
+ subject(:adapter) { described_class }
16
+
17
+ describe '#api' do
18
+ it 'when adapter' do
19
+ api = adapter.api(2)
20
+ expect(api).to be_a_kind_of(DoSnapshot::Adapter::DigitaloceanV2)
21
+ end
22
+
23
+ it 'when custom adapter' do
24
+ api = adapter.api('CustomAdapter')
25
+ expect(api).to be_a_kind_of(DoSnapshot::Adapter::CustomAdapter)
26
+ end
27
+
28
+ it 'when wrong custom adapter' do
29
+ expect { adapter.api('CustomAdapter2') }.to raise_exception(DoSnapshot::NoProtocolError)
30
+ end
31
+
32
+ it 'when error' do
33
+ expect { adapter.api(1) }.to raise_exception(DoSnapshot::NoProtocolError)
34
+ end
35
+ end
36
+ end
@@ -2,11 +2,11 @@
2
2
  require 'spec_helper'
3
3
 
4
4
  RSpec.describe DoSnapshot::CLI do
5
- include_context 'spec'
5
+ include_context 'environment'
6
6
  include_context 'api_v2_helpers'
7
7
 
8
8
  subject(:cli) { described_class }
9
- subject(:api) { DoSnapshot::Adapter::Digitalocean }
9
+ subject(:api) { DoSnapshot::Adapter::DigitaloceanV2 }
10
10
 
11
11
  describe '.initialize' do
12
12
  it 'with args & options' do
@@ -2,16 +2,12 @@
2
2
  require 'spec_helper'
3
3
 
4
4
  RSpec.describe DoSnapshot::Command do
5
- include_context 'spec'
5
+ include_context 'environment'
6
6
  include_context 'uri_helpers'
7
7
 
8
8
  subject(:cmd) { DoSnapshot::Command.new }
9
9
  subject(:log) { DoSnapshot::Log }
10
10
 
11
- describe 'V1' do
12
- include_context 'api_v1_helpers'
13
- end
14
-
15
11
  describe 'V2' do
16
12
  include_context 'api_v2_helpers'
17
13
 
@@ -2,40 +2,16 @@
2
2
  require 'spec_helper'
3
3
 
4
4
  RSpec.describe DoSnapshot::Log do
5
- include_context 'spec'
5
+ include_context 'environment'
6
6
 
7
7
  subject(:log) { described_class }
8
8
 
9
9
  describe 'will have message' do
10
- it '#info' do
11
- expect(DoSnapshot.logger).to respond_to(:info)
12
- DoSnapshot.logger.info('fff')
13
- expect(DoSnapshot.logger.buffer).to include('fff')
14
- end
15
-
16
- it '#debug' do
17
- expect(DoSnapshot.logger).to respond_to(:debug)
18
- DoSnapshot.logger.info('fff')
19
- expect(DoSnapshot.logger.buffer).to include('fff')
20
- end
21
-
22
- it '#warn' do
23
- expect(DoSnapshot.logger).to respond_to(:warn)
24
- DoSnapshot.logger.info('fff')
25
- expect(DoSnapshot.logger.buffer).to include('fff')
26
- end
27
-
28
- it '#fatal' do
29
- expect(DoSnapshot.logger).to respond_to(:fatal)
30
- DoSnapshot.logger.info('fff')
31
- expect(DoSnapshot.logger.buffer).to include('fff')
32
- end
33
-
34
- it '#error' do
35
- expect(DoSnapshot.logger).to respond_to(:error)
36
- DoSnapshot.logger.info('fff')
37
- expect(DoSnapshot.logger.buffer).to include('fff')
38
- end
10
+ it('#info') { logger_respond_to(:info) }
11
+ it('#debug') { logger_respond_to(:debug) }
12
+ it('#warn') { logger_respond_to(:warn) }
13
+ it('#fatal') { logger_respond_to(:fatal) }
14
+ it('#error') { logger_respond_to(:error) }
39
15
 
40
16
  it '#blablabla' do
41
17
  expect(DoSnapshot.logger).not_to respond_to(:blablabla)
@@ -49,6 +25,12 @@ RSpec.describe DoSnapshot::Log do
49
25
  end
50
26
  DoSnapshot.logger = DoSnapshot::Log.new
51
27
  end
28
+
29
+ def logger_respond_to(type)
30
+ expect(DoSnapshot.logger).to respond_to(type)
31
+ DoSnapshot.logger.send(type, 'fff')
32
+ expect(DoSnapshot.logger.buffer).to include('fff')
33
+ end
52
34
  end
53
35
 
54
36
  describe 'will work with files' do
@@ -2,7 +2,7 @@
2
2
  require 'spec_helper'
3
3
 
4
4
  RSpec.describe DoSnapshot::Mail do
5
- include_context 'spec'
5
+ include_context 'environment'
6
6
 
7
7
  subject(:mail) { described_class }
8
8
 
@@ -2,7 +2,7 @@
2
2
  require 'spec_helper'
3
3
 
4
4
  RSpec.describe DoSnapshot::Runner, type: :aruba do
5
- include_context 'spec'
5
+ include_context 'environment'
6
6
 
7
7
  context 'commands' do
8
8
  context '.snap' do
@@ -211,35 +211,6 @@ RSpec.describe DoSnapshot::Runner, type: :aruba do
211
211
  end
212
212
  end
213
213
 
214
- context 'API V1' do
215
- let(:default_options_cli) { default_options.merge(protocol: 1) }
216
- let(:snapshot_name) { "foo_#{DateTime.now.strftime('%Y_%m_%d')}" }
217
-
218
- include_context 'api_v1_helpers'
219
- it_behaves_like '.snap methods'
220
-
221
- context 'when no credentials' do
222
- it 'with warning about digitalocean credentials' do
223
- with_environment(cli_env_nil) do
224
- run "do_snapshot snap #{options_line}"
225
-
226
- expect(last_command).to have_exit_status(1)
227
- expect(all_stdout)
228
- .to include(t_wrong_keys(%w( digital_ocean_client_id digital_ocean_api_key ).join(', ')))
229
- end
230
- end
231
- end
232
-
233
- context 'when different credentials' do
234
- let(:keys_uri) { "api_key=#{cli_keys_other[:digital_ocean_api_key]}&client_id=#{cli_keys_other[:digital_ocean_client_id]}" }
235
-
236
- it 'with no warning' do
237
- hash_attribute_eq(cli_keys_other)
238
-
239
- expect(last_command).to have_exit_status(0)
240
- end
241
- end
242
- end
243
214
  end
244
215
 
245
216
  context '.help' do
@@ -2,7 +2,7 @@
2
2
  require 'spec_helper'
3
3
 
4
4
  RSpec.describe DoSnapshot do
5
- include_context 'spec'
5
+ include_context 'environment'
6
6
 
7
7
  describe DoSnapshot::DropletFindError do
8
8
  subject(:error) { described_class }
@@ -1,7 +1,7 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
  require 'spec_helper'
3
3
 
4
- shared_context 'api_helpers' do
4
+ RSpec.shared_context 'api_helpers' do
5
5
  # Stub helpers
6
6
  #
7
7
  def stub_with_id(request, id, fixture, status = 200)
@@ -1,7 +1,7 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
  require 'spec_helper'
3
3
 
4
- shared_context 'api_v2_helpers' do
4
+ RSpec.shared_context 'api_v2_helpers' do
5
5
  let(:api_base) { 'https://api.digitalocean.com/v2' }
6
6
  let(:droplets_api_base) { "#{api_base}/droplets" }
7
7
  let(:api_access_token) { "Bearer #{access_token}" }
@@ -1,7 +1,7 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
  require 'spec_helper'
3
3
 
4
- shared_context 'spec' do
4
+ RSpec.shared_context 'environment' do
5
5
  include_context 'api_helpers'
6
6
 
7
7
  def do_not_send_email
@@ -1,7 +1,7 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
  require 'spec_helper'
3
3
 
4
- shared_context 'uri_helpers' do
4
+ RSpec.shared_context 'uri_helpers' do
5
5
  let(:droplet_url) { url_with_id(droplet_find_uri, droplet_id) }
6
6
  let(:droplet_stop_url) { url_with_id(droplet_stop_uri, droplet_id) }
7
7
  let(:droplet_start_url) { url_with_id(droplet_start_uri, droplet_id) }
@@ -4,12 +4,14 @@ Coveralls.wear! do
4
4
  add_filter '/spec/*'
5
5
  end
6
6
 
7
+ require 'bundler'
8
+ Bundler.setup
9
+
7
10
  require 'do_snapshot/cli'
8
11
  require 'webmock/rspec'
9
12
  require 'fileutils'
10
13
  require 'digitalocean_c'
11
14
  require_relative 'shared/api_helpers'
12
- require_relative 'shared/api_v1_helpers'
13
15
  require_relative 'shared/api_v2_helpers'
14
16
  require_relative 'shared/uri_helpers'
15
17
  require_relative 'shared/environment'
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.5.0
4
+ version: 0.6.0
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-10-17 00:00:00.000000000 Z
11
+ date: 2016-01-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: digitalocean_c
@@ -80,7 +80,6 @@ files:
80
80
  - lib/do_snapshot.rb
81
81
  - lib/do_snapshot/adapter.rb
82
82
  - lib/do_snapshot/adapter/abstract.rb
83
- - lib/do_snapshot/adapter/digitalocean.rb
84
83
  - lib/do_snapshot/adapter/digitalocean_v2.rb
85
84
  - lib/do_snapshot/cli.rb
86
85
  - lib/do_snapshot/command.rb
@@ -94,8 +93,8 @@ files:
94
93
  - lib/do_snapshot/version.rb
95
94
  - spec/.keep
96
95
  - spec/do_snapshot/adapter/abstract_spec.rb
97
- - spec/do_snapshot/adapter/digitalocean_spec.rb
98
96
  - spec/do_snapshot/adapter/digitalocean_v2_spec.rb
97
+ - spec/do_snapshot/adapter_spec.rb
99
98
  - spec/do_snapshot/cli_spec.rb
100
99
  - spec/do_snapshot/command_spec.rb
101
100
  - spec/do_snapshot/configuration_spec.rb
@@ -103,14 +102,6 @@ files:
103
102
  - spec/do_snapshot/mail_spec.rb
104
103
  - spec/do_snapshot/runner_spec.rb
105
104
  - spec/do_snapshot_spec.rb
106
- - spec/fixtures/digitalocean/v1/error_message.json
107
- - spec/fixtures/digitalocean/v1/response_event.json
108
- - spec/fixtures/digitalocean/v1/show_droplet.json
109
- - spec/fixtures/digitalocean/v1/show_droplet_inactive.json
110
- - spec/fixtures/digitalocean/v1/show_droplets.json
111
- - spec/fixtures/digitalocean/v1/show_droplets_empty.json
112
- - spec/fixtures/digitalocean/v1/show_event_done.json
113
- - spec/fixtures/digitalocean/v1/show_event_start.json
114
105
  - spec/fixtures/digitalocean/v2/empty.json
115
106
  - spec/fixtures/digitalocean/v2/error_message.json
116
107
  - spec/fixtures/digitalocean/v2/response_event.json
@@ -125,7 +116,6 @@ files:
125
116
  - spec/fixtures/digitalocean/v2/show_event_power_on_start.json
126
117
  - spec/fixtures/digitalocean/v2/show_event_start.json
127
118
  - spec/shared/api_helpers.rb
128
- - spec/shared/api_v1_helpers.rb
129
119
  - spec/shared/api_v2_helpers.rb
130
120
  - spec/shared/environment.rb
131
121
  - spec/shared/uri_helpers.rb
@@ -160,8 +150,8 @@ summary: A command-line snapshot maker for your DigitalOcean droplets. Fully Aut
160
150
  test_files:
161
151
  - spec/.keep
162
152
  - spec/do_snapshot/adapter/abstract_spec.rb
163
- - spec/do_snapshot/adapter/digitalocean_spec.rb
164
153
  - spec/do_snapshot/adapter/digitalocean_v2_spec.rb
154
+ - spec/do_snapshot/adapter_spec.rb
165
155
  - spec/do_snapshot/cli_spec.rb
166
156
  - spec/do_snapshot/command_spec.rb
167
157
  - spec/do_snapshot/configuration_spec.rb
@@ -169,14 +159,6 @@ test_files:
169
159
  - spec/do_snapshot/mail_spec.rb
170
160
  - spec/do_snapshot/runner_spec.rb
171
161
  - spec/do_snapshot_spec.rb
172
- - spec/fixtures/digitalocean/v1/error_message.json
173
- - spec/fixtures/digitalocean/v1/response_event.json
174
- - spec/fixtures/digitalocean/v1/show_droplet.json
175
- - spec/fixtures/digitalocean/v1/show_droplet_inactive.json
176
- - spec/fixtures/digitalocean/v1/show_droplets.json
177
- - spec/fixtures/digitalocean/v1/show_droplets_empty.json
178
- - spec/fixtures/digitalocean/v1/show_event_done.json
179
- - spec/fixtures/digitalocean/v1/show_event_start.json
180
162
  - spec/fixtures/digitalocean/v2/empty.json
181
163
  - spec/fixtures/digitalocean/v2/error_message.json
182
164
  - spec/fixtures/digitalocean/v2/response_event.json
@@ -191,7 +173,6 @@ test_files:
191
173
  - spec/fixtures/digitalocean/v2/show_event_power_on_start.json
192
174
  - spec/fixtures/digitalocean/v2/show_event_start.json
193
175
  - spec/shared/api_helpers.rb
194
- - spec/shared/api_v1_helpers.rb
195
176
  - spec/shared/api_v2_helpers.rb
196
177
  - spec/shared/environment.rb
197
178
  - spec/shared/uri_helpers.rb
@@ -1,123 +0,0 @@
1
- # -*- encoding : utf-8 -*-
2
- require 'digitalocean_c' unless defined?(::DigitaloceanC)
3
-
4
- module DoSnapshot
5
- module Adapter
6
- # API for CLI commands
7
- # Operating with Digital Ocean.
8
- #
9
- class Digitalocean < Abstract
10
- # Get single droplet from DigitalOcean
11
- #
12
- def droplet(id)
13
- # noinspection RubyResolve
14
- response = ::DigitaloceanC::Droplet.find(id)
15
- fail DropletFindError.new(id), response.message unless response.status.include? 'OK'
16
- response.droplet
17
- end
18
-
19
- # Get droplets list from DigitalOcean
20
- #
21
- def droplets
22
- # noinspection RubyResolve
23
- response = ::DigitaloceanC::Droplet.all
24
- fail DropletListError, response.message unless response.status.include? 'OK'
25
- response.droplets
26
- end
27
-
28
- def snapshots(instance)
29
- instance.snapshots
30
- end
31
-
32
- # Request Power On for droplet
33
- #
34
- def power_on(id)
35
- # noinspection RubyResolve
36
- event = ::DigitaloceanC::Droplet.power_on(id)
37
- case event && event.status
38
- when 'OK'
39
- logger.info "Droplet id: #{id} is requested for Power On."
40
- else
41
- logger.error "Droplet id: #{id} is failed to request for Power On."
42
- end
43
- end
44
-
45
- # Power Off request for Droplet
46
- #
47
- def stop_droplet(id)
48
- # noinspection RubyResolve,RubyResolve
49
- event = ::DigitaloceanC::Droplet.power_off(id)
50
-
51
- fail event.message unless event.status.include? 'OK'
52
-
53
- # noinspection RubyResolve
54
- wait_shutdown(id, event.event_id)
55
- rescue => e
56
- raise DropletShutdownError.new(id), e.message, e.backtrace
57
- end
58
-
59
- # Sending event to create snapshot via DigitalOcean API and wait for success
60
- #
61
- def create_snapshot(id, name)
62
- # noinspection RubyResolve,RubyResolve
63
- event = ::DigitaloceanC::Droplet.snapshot(id, name: name)
64
-
65
- if !event
66
- fail DoSnapshot::SnapshotCreateError.new(id), 'Something wrong with DigitalOcean or with your connection :)'
67
- elsif event && !event.status.include?('OK')
68
- fail DoSnapshot::SnapshotCreateError.new(id), event.message
69
- end
70
-
71
- # noinspection RubyResolve
72
- wait_event(event.event_id)
73
- end
74
-
75
- # Checking if droplet is powered off.
76
- #
77
- def inactive?(id)
78
- instance = droplet(id)
79
-
80
- instance.status.include?('off')
81
- end
82
-
83
- # Cleanup our snapshots.
84
- #
85
- def cleanup_snapshots(instance, size)
86
- (0..size).each do |i|
87
- # noinspection RubyResolve
88
- snapshot = instance.snapshots[i]
89
- event = ::DigitaloceanC::Image.destroy(snapshot.id)
90
-
91
- after_cleanup(instance.id, instance.name, snapshot, event)
92
- end
93
- end
94
-
95
- def check_keys
96
- logger.debug 'Checking DigitalOcean Id\'s.'
97
- errors = %w( DIGITAL_OCEAN_CLIENT_ID DIGITAL_OCEAN_API_KEY ).map { |key| key if ENV[key].blank? }.compact
98
- fail DoSnapshot::NoKeysError, "You must have #{errors.join(', ')} in environment or set it via options." if errors.size > 0
99
- end
100
-
101
- protected
102
-
103
- # Set id's of Digital Ocean API.
104
- #
105
- def set_id
106
- logger.debug 'Setting DigitalOcean Id\'s.'
107
- ::DigitaloceanC.client_id = ENV['DIGITAL_OCEAN_CLIENT_ID']
108
- ::DigitaloceanC.api_key = ENV['DIGITAL_OCEAN_API_KEY']
109
- end
110
-
111
- # Looking for event status.
112
- #
113
- def get_event_status(id, time)
114
- return true if timeout?(id, time)
115
-
116
- event = ::DigitaloceanC::Event.find(id)
117
- fail DoSnapshot::EventError.new(id), event.message unless event.status.include?('OK')
118
- # noinspection RubyResolve,RubyResolve
119
- event.event.percentage && event.event.percentage.include?('100') ? true : false
120
- end
121
- end
122
- end
123
- end
@@ -1,265 +0,0 @@
1
- # -*- encoding : utf-8 -*-
2
- require 'spec_helper'
3
-
4
- RSpec.describe DoSnapshot::Adapter::Digitalocean do
5
- include_context 'spec'
6
- include_context 'api_v1_helpers'
7
-
8
- subject(:api) { described_class }
9
- subject(:log) { DoSnapshot::Log }
10
-
11
- describe '.initialize' do
12
- describe '#delay' do
13
- let(:delay) { 5 }
14
- let(:instance) { api.new(delay: delay) }
15
- it('with custom delay') { expect(instance.delay).to eq delay }
16
- end
17
-
18
- describe '#timeout' do
19
- let(:timeout) { 5 }
20
- let(:instance) { api.new(timeout: timeout) }
21
- it('with custom timeout') { expect(instance.timeout).to eq timeout }
22
- end
23
- end
24
-
25
- describe 'droplets' do
26
- let(:instance) { api.new(delay: delay, timeout: timeout) }
27
- include_context 'uri_helpers'
28
-
29
- describe '.droplet' do
30
- it 'with droplet' do
31
- stub_droplet(droplet_id)
32
-
33
- instance.droplet(droplet_id)
34
-
35
- expect(a_request(:get, droplet_url))
36
- .to have_been_made
37
- end
38
-
39
- it 'with error' do
40
- stub_droplet_fail(droplet_id)
41
-
42
- expect { instance.droplet(droplet_id) }
43
- .to raise_error(DoSnapshot::DropletFindError)
44
- expect(DoSnapshot.logger.buffer)
45
- .to include "Droplet id: #{droplet_id} Not Found"
46
-
47
- expect(a_request(:get, droplet_url))
48
- .to have_been_made
49
- end
50
- end
51
-
52
- describe '.droplets' do
53
- it 'with droplets' do
54
- stub_droplets
55
-
56
- instance.droplets
57
-
58
- expect(a_request(:get, droplets_uri))
59
- .to have_been_made
60
- end
61
-
62
- it 'with error' do
63
- stub_droplets_fail
64
-
65
- expect { instance.droplets }.to raise_error(DoSnapshot::DropletListError)
66
- expect(DoSnapshot.logger.buffer)
67
- .to include 'Droplet Listing is failed to retrieve'
68
-
69
- expect(a_request(:get, droplets_uri))
70
- .to have_been_made
71
- end
72
- end
73
-
74
- describe '.start_droplet' do
75
- it 'with event' do
76
- stub_droplet_inactive(droplet_id)
77
- stub_droplet_start(droplet_id)
78
-
79
- instance.start_droplet(droplet_id)
80
- expect(DoSnapshot.logger.buffer).to include "Droplet id: #{droplet_id} is requested for Power On."
81
-
82
- expect(a_request(:get, droplet_start_url))
83
- .to have_been_made
84
- expect(a_request(:get, droplet_url))
85
- .to have_been_made
86
- end
87
-
88
- it 'with warning message' do
89
- stub_droplet(droplet_id)
90
-
91
- expect { instance.start_droplet(droplet_id) }
92
- .not_to raise_error
93
- expect(DoSnapshot.logger.buffer)
94
- .to include "Droplet #{droplet_id} is still running. Skipping."
95
-
96
- expect(a_request(:get, droplet_url))
97
- .to have_been_made
98
- end
99
-
100
- it 'with error' do
101
- stub_droplet_fail(droplet_id)
102
-
103
- expect { instance.start_droplet(droplet_id) }
104
- .to raise_error(DoSnapshot::DropletFindError)
105
-
106
- expect(a_request(:get, droplet_url))
107
- .to have_been_made
108
- end
109
- end
110
-
111
- describe '.stop_droplet by power status' do
112
- let(:instance) { api.new(delay: delay, timeout: timeout, stop_by: :power_status) }
113
-
114
- it 'with success' do
115
- stub_event_done(event_id)
116
- stub_droplet_stop(droplet_id)
117
- stub_droplet_inactive(droplet_id)
118
-
119
- instance.stop_droplet(droplet_id)
120
-
121
- expect(a_request(:get, droplet_stop_url))
122
- .to have_been_made
123
- expect(a_request(:get, droplet_url))
124
- .to have_been_made
125
- end
126
-
127
- it 'with error' do
128
- stub_droplet_stop_fail(droplet_id)
129
- stub_droplet(droplet_id)
130
-
131
- instance.timeout = 1
132
- expect { instance.stop_droplet(droplet_id) }
133
- .to raise_error(DoSnapshot::DropletShutdownError)
134
- instance.timeout = timeout
135
- expect(DoSnapshot.logger.buffer)
136
- .to include 'Droplet id: 100823 is Failed to Power Off.'
137
-
138
- expect(a_request(:get, droplet_stop_url))
139
- .to have_been_made
140
- end
141
- end
142
-
143
- describe '.stop_droplet by event' do
144
- it 'with success' do
145
- stub_event_done(event_id)
146
- stub_droplet_stop(droplet_id)
147
-
148
- instance.stop_droplet(droplet_id)
149
-
150
- expect(a_request(:get, droplet_stop_url))
151
- .to have_been_made
152
- expect(a_request(:get, event_find_url))
153
- .to have_been_made
154
- end
155
-
156
- it 'with error' do
157
- stub_droplet_stop_fail(droplet_id)
158
-
159
- expect { instance.stop_droplet(droplet_id) }
160
- .to raise_error(DoSnapshot::DropletShutdownError)
161
- expect(DoSnapshot.logger.buffer)
162
- .to include 'Droplet id: 100823 is Failed to Power Off.'
163
-
164
- expect(a_request(:get, droplet_stop_url))
165
- .to have_been_made
166
- end
167
- end
168
-
169
- describe '.create_snapshot' do
170
- it 'with success' do
171
- stub_event_done(event_id)
172
- stub_droplet_snapshot(droplet_id, snapshot_name)
173
-
174
- expect { instance.create_snapshot(droplet_id, snapshot_name) }
175
- .not_to raise_error
176
-
177
- expect(a_request(:get, droplet_snapshot_url))
178
- .to have_been_made
179
- expect(a_request(:get, event_find_url))
180
- .to have_been_made
181
- end
182
-
183
- it 'with error' do
184
- stub_droplet_snapshot_fail(droplet_id, snapshot_name)
185
-
186
- expect { instance.create_snapshot(droplet_id, snapshot_name) }
187
- .to raise_error(DoSnapshot::SnapshotCreateError)
188
-
189
- expect(a_request(:get, droplet_snapshot_url))
190
- .to have_been_made
191
- end
192
-
193
- it 'with event error' do
194
- stub_droplet_snapshot(droplet_id, snapshot_name)
195
- stub_event_fail(event_id)
196
-
197
- expect { instance.create_snapshot(droplet_id, snapshot_name) }
198
- .not_to raise_error
199
- expect(DoSnapshot.logger.buffer)
200
- .to include "Event id: #{event_id} is failed!"
201
-
202
- expect(a_request(:get, droplet_snapshot_url))
203
- .to have_been_made
204
- expect(a_request(:get, event_find_url))
205
- .to have_been_made
206
- end
207
- end
208
-
209
- describe '.inactive?' do
210
- it 'when inactive' do
211
- stub_droplet_inactive(droplet_id)
212
-
213
- expect(instance.inactive?(droplet_id))
214
- .to be_truthy
215
- end
216
-
217
- it 'when active' do
218
- stub_droplet(droplet_id)
219
-
220
- expect(instance.inactive?(droplet_id))
221
- .to be_falsey
222
- end
223
- end
224
-
225
- describe '.cleanup_snapshots' do
226
- it 'with success' do
227
- stub_droplet(droplet_id)
228
- stub_image_destroy(image_id)
229
- stub_image_destroy(image_id2)
230
-
231
- droplet = instance.droplet(droplet_id)
232
- expect { instance.cleanup_snapshots(droplet, 1) }
233
- .not_to raise_error
234
- expect(DoSnapshot.logger.buffer)
235
- .to include 'Snapshot name: mrcr.ru_2014_07_19 delete requested.'
236
-
237
- expect(a_request(:get, droplet_url))
238
- .to have_been_made
239
- expect(a_request(:get, image_destroy_url))
240
- .to have_been_made
241
- expect(a_request(:get, image_destroy2_url))
242
- .to have_been_made
243
- end
244
-
245
- it 'with warning message' do
246
- stub_droplet(droplet_id)
247
- stub_image_destroy_fail(image_id)
248
- stub_image_destroy_fail(image_id2)
249
-
250
- droplet = instance.droplet(droplet_id)
251
- expect { instance.cleanup_snapshots(droplet, 1) }
252
- .not_to raise_error
253
- expect(DoSnapshot.logger.buffer)
254
- .to include 'Some Message'
255
-
256
- expect(a_request(:get, droplet_url))
257
- .to have_been_made
258
- expect(a_request(:get, image_destroy_url))
259
- .to have_been_made
260
- expect(a_request(:get, image_destroy2_url))
261
- .to have_been_made
262
- end
263
- end
264
- end
265
- end
@@ -1,4 +0,0 @@
1
- {
2
- "status": "ERROR",
3
- "message": "Some Message"
4
- }
@@ -1,4 +0,0 @@
1
- {
2
- "status": "OK",
3
- "event_id": 7499
4
- }
@@ -1,39 +0,0 @@
1
- {
2
- "status": "OK",
3
- "droplet": {
4
- "backups_active": null,
5
- "id": 100823,
6
- "image_id": 420,
7
- "name": "foo",
8
- "ip_address": "33.33.33.10",
9
- "snapshots": [
10
- {
11
- "id": 5019770,
12
- "name": "mrcr.ru_2014_07_19",
13
- "slug": null,
14
- "distribution": "CentOS"
15
- },
16
- {
17
- "id": 5019903,
18
- "name": "mrcr.ru_2014_07_19",
19
- "slug": null,
20
- "distribution": "CentOS"
21
- },
22
- {
23
- "id": 5020783,
24
- "name": "mrcr.ru_2014_07_19",
25
- "slug": null,
26
- "distribution": "CentOS"
27
- },
28
- {
29
- "id": 5030783,
30
- "name": "mrcr.ru_2014_07_19",
31
- "slug": null,
32
- "distribution": "CentOS"
33
- }
34
- ],
35
- "region_id": 1,
36
- "size_id": 33,
37
- "status": "active"
38
- }
39
- }
@@ -1,39 +0,0 @@
1
- {
2
- "status": "OK",
3
- "droplet": {
4
- "backups_active": null,
5
- "id": 100823,
6
- "image_id": 420,
7
- "name": "foo",
8
- "ip_address": "33.33.33.10",
9
- "snapshots": [
10
- {
11
- "id": 5019770,
12
- "name": "mrcr.ru_2014_07_19",
13
- "slug": null,
14
- "distribution": "CentOS"
15
- },
16
- {
17
- "id": 5019903,
18
- "name": "mrcr.ru_2014_07_19",
19
- "slug": null,
20
- "distribution": "CentOS"
21
- },
22
- {
23
- "id": 5020783,
24
- "name": "mrcr.ru_2014_07_19",
25
- "slug": null,
26
- "distribution": "CentOS"
27
- },
28
- {
29
- "id": 5030783,
30
- "name": "mrcr.ru_2014_07_19",
31
- "slug": null,
32
- "distribution": "CentOS"
33
- }
34
- ],
35
- "region_id": 1,
36
- "size_id": 33,
37
- "status": "off"
38
- }
39
- }
@@ -1,35 +0,0 @@
1
- {
2
- "status": "OK",
3
- "droplets": [
4
- {
5
- "ip_address": "33.33.33.10",
6
- "backups_active": null,
7
- "id": 100823,
8
- "image_id": 420,
9
- "name": "test222",
10
- "region_id": 1,
11
- "size_id": 33,
12
- "status": "active"
13
- },
14
- {
15
- "ip_address": "33.33.33.10",
16
- "backups_active": null,
17
- "id": 100824,
18
- "image_id": 420,
19
- "name": "test223",
20
- "region_id": 1,
21
- "size_id": 33,
22
- "status": "active"
23
- },
24
- {
25
- "ip_address": "33.33.33.10",
26
- "backups_active": null,
27
- "id": 100825,
28
- "image_id": 420,
29
- "name": "foo",
30
- "region_id": 1,
31
- "size_id": 33,
32
- "status": "active"
33
- }
34
- ]
35
- }
@@ -1,4 +0,0 @@
1
- {
2
- "status":"OK",
3
- "droplets":[]
4
- }
@@ -1,10 +0,0 @@
1
- {
2
- "status": "OK",
3
- "event": {
4
- "id": 7499,
5
- "action_status": "done",
6
- "droplet_id": 100823,
7
- "event_type_id": 1,
8
- "percentage": "100"
9
- }
10
- }
@@ -1,10 +0,0 @@
1
- {
2
- "status": "OK",
3
- "event": {
4
- "id": 7499,
5
- "action_status": "done",
6
- "droplet_id": 100823,
7
- "event_type_id": 1,
8
- "percentage": "3"
9
- }
10
- }
@@ -1,97 +0,0 @@
1
- # -*- encoding : utf-8 -*-
2
- require 'spec_helper'
3
-
4
- shared_context 'api_v1_helpers' do
5
- let(:api_base) { 'https://api.digitalocean.com/v1' }
6
- let(:keys_uri) { "api_key=#{api_key}&client_id=#{client_key}" }
7
- let(:droplets_api_base) { "#{api_base}/droplets" }
8
- let(:events_api_base) { "#{api_base}/events" }
9
- let(:images_api_base) { "#{api_base}/images" }
10
- let(:image_destroy_uri) { "#{images_api_base}/[id]/destroy/?#{keys_uri}" }
11
- let(:droplets_uri) { "#{droplets_api_base}/?#{keys_uri}" }
12
- let(:droplet_find_uri) { "#{droplets_api_base}/[id]?#{keys_uri}" }
13
- let(:droplet_stop_uri) { "#{droplets_api_base}/[id]/power_off/?#{keys_uri}" }
14
- let(:droplet_start_uri) { "#{droplets_api_base}/[id]/power_on/?#{keys_uri}" }
15
- let(:snapshot_uri) { "#{droplets_api_base}/[id]/snapshot/?name=[name]&#{keys_uri}" }
16
- let(:event_find_uri) { "#{events_api_base}/[id]/?#{keys_uri}" }
17
-
18
- # List of droplets
19
- #
20
- def stub_droplets
21
- stub_without_id(droplets_uri, 'v1/show_droplets')
22
- end
23
-
24
- def stub_droplets_empty
25
- stub_without_id(droplets_uri, 'v1/show_droplets_empty')
26
- end
27
-
28
- def stub_droplets_fail
29
- stub_without_id(droplets_uri, 'v1/error_message')
30
- end
31
-
32
- # Droplet data
33
- #
34
- def stub_droplet(id)
35
- stub_with_id(droplet_find_uri, id, 'v1/show_droplet')
36
- end
37
-
38
- def stub_droplet_fail(id)
39
- stub_with_id(droplet_find_uri, id, 'v1/error_message')
40
- end
41
-
42
- def stub_droplet_inactive(id)
43
- stub_with_id(droplet_find_uri, id, 'v1/show_droplet_inactive')
44
- end
45
-
46
- # Droplet actions
47
- #
48
- def stub_droplet_stop(id)
49
- stub_with_id(droplet_stop_uri, id, 'v1/response_event')
50
- end
51
-
52
- def stub_droplet_stop_fail(id)
53
- stub_with_id(droplet_stop_uri, id, 'v1/error_message')
54
- end
55
-
56
- def stub_droplet_start(id)
57
- stub_with_id(droplet_start_uri, id, 'v1/response_event')
58
- end
59
-
60
- def stub_droplet_start_fail(id)
61
- stub_with_id(droplet_start_uri, id, 'v1/error_message')
62
- end
63
-
64
- # Snapshot
65
- #
66
- def stub_droplet_snapshot(id, name)
67
- stub_with_id_name(snapshot_uri, id, name, 'v1/response_event')
68
- end
69
-
70
- def stub_droplet_snapshot_fail(id, name)
71
- stub_with_id_name(snapshot_uri, id, name, 'v1/error_message')
72
- end
73
-
74
- # Event status
75
- #
76
- def stub_event_done(id)
77
- stub_with_id(event_find_uri, id, 'v1/show_event_done')
78
- end
79
-
80
- def stub_event_fail(id)
81
- stub_with_id(event_find_uri, id, 'v1/error_message')
82
- end
83
-
84
- def stub_event_running(id)
85
- stub_with_id(event_find_uri, id, 'v1/show_event_running')
86
- end
87
-
88
- # Image actions
89
- #
90
- def stub_image_destroy(id)
91
- stub_with_id(image_destroy_uri, id, 'v1/response_event')
92
- end
93
-
94
- def stub_image_destroy_fail(id)
95
- stub_with_id(image_destroy_uri, id, 'v1/error_message')
96
- end
97
- end