linodeapi 1.0.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d2b6266cebf4ba879b7b79560958046dcfbeef85
4
- data.tar.gz: e4f7be859b3b424d4023d228a2bf465652b27c8d
3
+ metadata.gz: 386eb467c95101a915f20e0859bf356bb8c765ed
4
+ data.tar.gz: 45437aba6937cb0e0a03de655948479d5938241d
5
5
  SHA512:
6
- metadata.gz: 9c0400892e248bf04836685ea695ed2415dd5c38b39fd12f55028685a178d3cc6891ac99c07a6c730b2ada07c19058a86dad965bfc3ad472c19a7cb0df65586c
7
- data.tar.gz: a3645671fea6219314385c06f922eb957796460beb4f864a8df72700e2210d306dd699d39c00079791fb7b3546256284d6337b386180b3f2d942642bfe3bd1a6
6
+ metadata.gz: 18391de6e8204d94eef64f38b28a6f5fba0e0c3f37bb948029237cdeebfbbddcd00df70e107c88450c14e6700f2c1cb9b1c257d968cd5d6b87586dd27cfff1ed
7
+ data.tar.gz: a01e3df48784a83b12a8cabf96e0608505c825d56a7fd4a81a7ebd330fd41d18a26ec1d7134df3b69a23cc0191c0d37e89f0e5b294c93b3f34e35df9618569f5
@@ -0,0 +1,3 @@
1
+ 2.4.2
2
+ 2.3.5
3
+ 2.2.8
@@ -1,31 +1,25 @@
1
- require 'yaml'
1
+ Prospectus.extra_dep('file', 'prospectus_circleci')
2
2
 
3
- item do
4
- expected do
5
- static
6
- set 'green'
7
- end
3
+ my_slug = 'akerl/linodeapi'
8
4
 
9
- actual do
10
- gemnasium
11
- slug 'akerl/linodeapi'
12
- end
5
+ item do
6
+ noop
13
7
 
14
8
  deps do
15
- YAML.load(`./dev/cache_spec.rb --noop`).each do |item_name, (old, new)|
16
- item do
17
- name "api.spec.#{item_name}"
9
+ item do
10
+ name 'gems'
18
11
 
19
- expected do
20
- static
21
- set new
22
- end
12
+ expected do
13
+ static
14
+ set 'green'
15
+ end
23
16
 
24
- actual do
25
- static
26
- set old
27
- end
17
+ actual do
18
+ gemnasium
19
+ slug my_slug
28
20
  end
29
21
  end
30
22
  end
23
+
24
+ extend ProspectusCircleci::Build.new(my_slug)
31
25
  end
@@ -1,2 +1,5 @@
1
- Encoding:
2
- Enabled: false
1
+ inherit_gem:
2
+ goodcop: .rubocop.yml
3
+ AllCops:
4
+ Include:
5
+ - 'dev/*.rb'
data/Gemfile CHANGED
@@ -1,4 +1,3 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
3
  gemspec
4
-
data/README.md CHANGED
@@ -3,7 +3,7 @@ linodeapi
3
3
 
4
4
  [![Gem Version](https://img.shields.io/gem/v/linodeapi.svg)](https://rubygems.org/gems/linodeapi)
5
5
  [![Dependency Status](https://img.shields.io/gemnasium/akerl/linodeapi.svg)](https://gemnasium.com/akerl/linodeapi)
6
- [![Build Status](https://img.shields.io/circleci/project/akerl/linodeapi.svg)](https://circleci.com/gh/akerl/linodeapi)
6
+ [![Build Status](https://img.shields.io/circleci/project/akerl/linodeapi/master.svg)](https://circleci.com/gh/akerl/linodeapi)
7
7
  [![Coverage Status](https://img.shields.io/codecov/c/github/akerl/linodeapi.svg)](https://codecov.io/github/akerl/linodeapi)
8
8
  [![Code Quality](https://img.shields.io/codacy/60b77ea8214241b5ac970bcdfd584587.svg)](https://www.codacy.com/app/akerl/linodeapi)
9
9
  [![MIT Licensed](https://img.shields.io/badge/license-MIT-green.svg)](https://tldrlegal.com/license/mit-license)
@@ -54,6 +54,12 @@ api.linode.create
54
54
  # ArgumentError: linode.create requires planid
55
55
  ```
56
56
 
57
+ ### Retryable helper
58
+
59
+ The LinodeAPI::Retryable class automatically handles 429 errors (really, any HTTP error that is accompanied by a Retry-After header).
60
+
61
+ This class behaves identially to the LinodeAPI::Raw class, with 2 additional parameters at creation: `max_retries` sets the total number of requests it will attempt per call and `max_delay` sets the maximum delay between attempts. They default to 3 tries and 60 seconds, respectively.
62
+
57
63
  ## Installation
58
64
 
59
65
  gem install linodeapi
data/Rakefile CHANGED
@@ -7,8 +7,7 @@ RSpec::Core::RakeTask.new(:spec)
7
7
 
8
8
  desc 'Run Rubocop on the gem'
9
9
  RuboCop::RakeTask.new(:rubocop) do |task|
10
- task.patterns = ['lib/**/*.rb', 'spec/**/*.rb', 'dev/*.rb']
11
10
  task.fail_on_error = true
12
11
  end
13
12
 
14
- task default: [:spec, :rubocop, :build, :install]
13
+ task default: %i[spec rubocop build install]
data/circle.yml CHANGED
@@ -1,12 +1,7 @@
1
1
  dependencies:
2
2
  override:
3
- - 'rvm-exec 1.9.3-p551 bundle install'
4
- - 'rvm-exec 2.0.0-p645 bundle install'
5
- - 'rvm-exec 2.1.6 bundle install'
6
- - 'rvm-exec 2.2.2 bundle install'
3
+ - 'for i in $(cat .circle-ruby) ; do rvm-exec $i gem update --system --no-doc || exit 1 ; done'
4
+ - 'for i in $(cat .circle-ruby) ; do rvm-exec $i bundle install || exit 1 ; done'
7
5
  test:
8
6
  override:
9
- - 'rvm-exec 1.9.3-p551 bundle exec rake'
10
- - 'rvm-exec 2.0.0-p645 bundle exec rake'
11
- - 'rvm-exec 2.1.6 bundle exec rake'
12
- - 'rvm-exec 2.2.2 bundle exec rake'
7
+ - 'for i in $(cat .circle-ruby) ; do rvm-exec $i bundle exec rake || exit 1 ; done'
@@ -686,39 +686,10 @@ linode:
686
686
  create:
687
687
  - desc: ''
688
688
  - params:
689
- - fromdistributionid:
690
- - desc: ''
691
- - required: false
692
- - type: :numeric
693
- - isreadonly:
694
- - desc: Enable forced read-only for this Disk
695
- - required: false
696
- - type: :boolean
697
- - label:
698
- - desc: The display label for this Disk
699
- - required: true
700
- - type: :string
701
689
  - linodeid:
702
690
  - desc: ''
703
691
  - required: true
704
692
  - type: :numeric
705
- - rootpass:
706
- - desc: ''
707
- - required: false
708
- - type: :string
709
- - rootsshkey:
710
- - desc: ''
711
- - required: false
712
- - type: :string
713
- - size:
714
- - desc: The size in MB of this Disk.
715
- - required: true
716
- - type: :numeric
717
- - type:
718
- - desc: 'The formatted type of this disk. Valid types are: ext3, ext4, swap,
719
- raw'
720
- - required: true
721
- - type: :string
722
693
  - throws:
723
694
  - NOTFOUND
724
695
  - VALIDATION
@@ -912,14 +883,6 @@ linode:
912
883
  - desc: ''
913
884
  - required: true
914
885
  - type: :numeric
915
- - isreadonly:
916
- - desc: Enable forced read-only for this Disk
917
- - required: false
918
- - type: :boolean
919
- - label:
920
- - desc: The display label for this Disk
921
- - required: false
922
- - type: :string
923
886
  - linodeid:
924
887
  - desc: ''
925
888
  - required: false
@@ -1016,6 +979,16 @@ linode:
1016
979
  - required: false
1017
980
  - type: :boolean
1018
981
  - throws: []
982
+ kvmify:
983
+ - desc: Changes a Linode's hypervisor from Xen to KVM.
984
+ - params:
985
+ - linodeid:
986
+ - desc: The LinodeID to migrate to KVM.
987
+ - required: true
988
+ - type: :numeric
989
+ - throws:
990
+ - NOTFOUND
991
+ - VALIDATION
1019
992
  list:
1020
993
  - desc: 'Returns a list of all Linodes user has access or delete to, including some
1021
994
  properties. Status values are -1: Being Created, 0: Brand New, 1: Running,
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'httparty'
3
4
 
4
5
  ##
@@ -74,4 +75,6 @@ module LinodeAPI
74
75
  end
75
76
  end
76
77
 
78
+ require 'linodeapi/errors'
77
79
  require 'linodeapi/raw'
80
+ require 'linodeapi/retryable'
@@ -0,0 +1,47 @@
1
+ module LinodeAPI
2
+ ##
3
+ # A standard HTTP error with an embedded error code
4
+ class HTTPError < StandardError
5
+ attr_reader :code
6
+
7
+ def initialize(code, msg = 'HTTP Error encountered')
8
+ @code = code
9
+ super(msg)
10
+ end
11
+ end
12
+
13
+ ##
14
+ # A retryable error that has exceeded its max retries
15
+ class RetriedHTTPError < HTTPError
16
+ attr_reader :retries
17
+
18
+ def initialize(code, retries, msg = nil)
19
+ @retries = retries
20
+ msg ||= "HTTP Error encountered (retried #{retries} times)"
21
+ super(code, msg)
22
+ end
23
+ end
24
+
25
+ ##
26
+ # A retryable API error with embedded code and requested delay
27
+ class RetryableHTTPError < HTTPError
28
+ attr_reader :delay
29
+
30
+ def initialize(code, delay, msg = 'Retryable HTTP Error encountered')
31
+ @delay = delay.to_i
32
+ super(code, msg)
33
+ end
34
+ end
35
+
36
+ ##
37
+ # An API error in the body of the HTTP response
38
+ class APIError < StandardError
39
+ attr_reader :action, :details
40
+
41
+ def initialize(resp, msg = 'API Error encountered')
42
+ @action = resp['ACTION']
43
+ @details = resp['ERRORARRAY']
44
+ super(msg)
45
+ end
46
+ end
47
+ end
@@ -9,17 +9,16 @@ module LinodeAPI
9
9
  class Raw
10
10
  include HTTParty
11
11
 
12
- attr_reader :apikey, :spec, :names
13
-
14
12
  def initialize(params = {})
13
+ @options = params
15
14
  self.class.base_uri params.fetch(:endpoint, DEFAULT_ENDPOINT)
16
- @names = params.fetch(:names) { [] }
17
- @spec = params.fetch(:spec) { LinodeAPI.spec }
18
- @apikey = params.fetch(:apikey) { authenticate(params) }
15
+ @options[:names] ||= []
16
+ @options[:spec] ||= LinodeAPI.spec
17
+ @options[:apikey] ||= authenticate(params)
19
18
  end
20
19
 
21
- def respond_to?(method, include_private = false)
22
- super || @spec[:subs].include?(method)
20
+ def respond_to_missing?(method, include_private = false)
21
+ spec[:subs].include?(method) || super
23
22
  end
24
23
 
25
24
  def to_s
@@ -27,10 +26,22 @@ module LinodeAPI
27
26
  end
28
27
  alias inspect to_s
29
28
 
29
+ def names
30
+ @names ||= @options[:names]
31
+ end
32
+
33
+ def spec
34
+ @spec ||= @options[:spec]
35
+ end
36
+
37
+ def apikey
38
+ @apikey ||= @options[:apikey]
39
+ end
40
+
30
41
  private
31
42
 
32
43
  def authenticate(params = {})
33
- return [] unless @names.empty?
44
+ return [] unless names.empty?
34
45
  unless (params.values_at :username, :password).all?
35
46
  raise ArgumentError, 'You must provide either an API key or user/pass'
36
47
  end
@@ -39,18 +50,18 @@ module LinodeAPI
39
50
 
40
51
  def method_missing(method, *args, &block)
41
52
  return super unless respond_to? method
42
- case @spec[:subs][method][:type]
53
+ case spec[:subs][method][:type]
43
54
  when :group then make_group method
44
55
  when :call then make_call method, *args
45
56
  end
46
57
  end
47
58
 
48
59
  def make_group(method)
49
- group = Raw.new(
50
- spec: @spec[:subs][method],
51
- apikey: @apikey,
52
- username: @username,
53
- names: @names + [method]
60
+ group = self.class.new(
61
+ @options.dup.merge(
62
+ spec: spec[:subs][method],
63
+ names: names + [method]
64
+ )
54
65
  )
55
66
  name = "@#{method}".to_sym
56
67
  instance_variable_set name, group
@@ -64,28 +75,34 @@ module LinodeAPI
64
75
  end
65
76
 
66
77
  def call(method, params = {})
67
- spec = @spec[:subs][method]
68
- method = (@names + [method.to_s]).join '.'
69
- options = self.class.validate method, spec[:params], params
70
- options[:api_key] = @apikey
78
+ mspec = spec[:subs][method]
79
+ method = (names + [method.to_s]).join '.'
80
+ options = self.class.validate method, mspec[:params], params
81
+ options[:api_key] = apikey
71
82
  options[:api_action] = method
72
83
  error_check self.class.post('', body: options)
73
84
  end
74
85
 
75
86
  def error_check(resp)
76
- code = resp.code
77
- raise("API threw HTTP error code #{code}") unless code == 200
87
+ error = create_http_error(resp)
88
+ raise(error) if error
78
89
  data = resp.parsed_response
79
90
  raise('Invalid API response received') if data.nil?
80
91
  self.class.parse data
81
92
  end
82
93
 
94
+ def create_http_error(resp)
95
+ code = resp.code
96
+ return nil if code == 200
97
+ delay = resp.headers['Retry-After']
98
+ return RetryableHTTPError.new(code, delay) if delay
99
+ HTTPError.new(code)
100
+ end
101
+
83
102
  class << self
84
103
  def parse(resp)
85
104
  resp['ERRORARRAY'].reject! { |x| x['ERRORCODE'].zero? }
86
- unless resp['ERRORARRAY'].empty?
87
- raise "API Error on #{resp['ACTION']}: #{resp['ERRORARRAY']}"
88
- end
105
+ raise(APIError, resp) unless resp['ERRORARRAY'].empty?
89
106
  data = resp['DATA']
90
107
  data.is_a?(Hash) ? clean(data) : data.map { |x| clean x }
91
108
  end
@@ -94,8 +111,8 @@ module LinodeAPI
94
111
  OpenStruct.new(Hash[object.map { |k, v| [k.downcase.to_sym, v] }])
95
112
  end
96
113
 
97
- def validate(method, spec, given)
98
- spec.each_with_object({}) do |(param, info), options|
114
+ def validate(method, mspec, given)
115
+ mspec.each_with_object({}) do |(param, info), options|
99
116
  if given.include? param
100
117
  options[param] = VALIDATION_METHODS[info[:type]].call given[param]
101
118
  elsif info[:required]
@@ -0,0 +1,36 @@
1
+ module LinodeAPI
2
+ ##
3
+ # A wrapper for the Raw API that can retry retryable errors
4
+ class Retryable < Raw
5
+ def initialize(params = {})
6
+ super
7
+ @options[:max_retries] ||= 3
8
+ @options[:max_delay] ||= 60
9
+ end
10
+
11
+ private
12
+
13
+ alias single_call call
14
+
15
+ def call(*args)
16
+ call_with_retries(max_retries, *args)
17
+ end
18
+
19
+ def call_with_retries(remaining, *args)
20
+ single_call(*args)
21
+ rescue RetryableHTTPError => e
22
+ raise(RetriedHTTPError.new(e.code, max_retries)) if remaining < 0
23
+ delay = [e.delay, max_delay].min
24
+ sleep delay
25
+ call_with_retries(remaining - 1, *args)
26
+ end
27
+
28
+ def max_retries
29
+ @options[:max_retries]
30
+ end
31
+
32
+ def max_delay
33
+ @options[:max_delay]
34
+ end
35
+ end
36
+ end
@@ -1,10 +1,10 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'linodeapi'
3
- s.version = '1.0.1'
4
- s.date = Time.now.strftime("%Y-%m-%d")
3
+ s.version = '2.0.0'
4
+ s.date = Time.now.strftime('%Y-%m-%d')
5
5
 
6
6
  s.summary = 'Linode API wrapper'
7
- s.description = "Wraps the Linode API with multiple levels of interaction"
7
+ s.description = 'Wraps the Linode API with multiple levels of interaction'
8
8
  s.authors = ['Les Aker']
9
9
  s.email = 'me@lesaker.org'
10
10
  s.homepage = 'https://github.com/akerl/linodeapi'
@@ -13,13 +13,14 @@ Gem::Specification.new do |s|
13
13
  s.files = `git ls-files`.split
14
14
  s.test_files = `git ls-files spec/*`.split
15
15
 
16
- s.add_dependency 'httparty', '~> 0.13.1'
16
+ s.add_dependency 'httparty', '~> 0.15.5'
17
17
 
18
- s.add_development_dependency 'rubocop', '~> 0.41.1'
19
- s.add_development_dependency 'rake', '~> 11.2.0'
18
+ s.add_development_dependency 'rubocop', '~> 0.50.0'
19
+ s.add_development_dependency 'goodcop', '~> 0.1.0'
20
+ s.add_development_dependency 'rake', '~> 12.1.0'
20
21
  s.add_development_dependency 'codecov', '~> 0.1.1'
21
- s.add_development_dependency 'rspec', '~> 3.5.0'
22
- s.add_development_dependency 'fuubar', '~> 2.0.0'
23
- s.add_development_dependency 'webmock', '~> 2.1.0'
22
+ s.add_development_dependency 'rspec', '~> 3.6.0'
23
+ s.add_development_dependency 'fuubar', '~> 2.2.0'
24
+ s.add_development_dependency 'webmock', '~> 3.0.0'
24
25
  s.add_development_dependency 'vcr', '~> 3.0.0'
25
26
  end
@@ -19,7 +19,7 @@ describe LinodeAPI::Raw do
19
19
  VCR.use_cassette('basic_auth_fail') do
20
20
  expect do
21
21
  LinodeAPI::Raw.new(username: 'bad', password: 'bad')
22
- end.to raise_error RuntimeError
22
+ end.to raise_error LinodeAPI::APIError
23
23
  end
24
24
  end
25
25
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: linodeapi
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Les Aker
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-07-01 00:00:00.000000000 Z
11
+ date: 2017-10-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: httparty
@@ -16,42 +16,56 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 0.13.1
19
+ version: 0.15.5
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 0.13.1
26
+ version: 0.15.5
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rubocop
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 0.41.1
33
+ version: 0.50.0
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 0.41.1
40
+ version: 0.50.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: goodcop
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.1.0
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.1.0
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: rake
43
57
  requirement: !ruby/object:Gem::Requirement
44
58
  requirements:
45
59
  - - "~>"
46
60
  - !ruby/object:Gem::Version
47
- version: 11.2.0
61
+ version: 12.1.0
48
62
  type: :development
49
63
  prerelease: false
50
64
  version_requirements: !ruby/object:Gem::Requirement
51
65
  requirements:
52
66
  - - "~>"
53
67
  - !ruby/object:Gem::Version
54
- version: 11.2.0
68
+ version: 12.1.0
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: codecov
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -72,42 +86,42 @@ dependencies:
72
86
  requirements:
73
87
  - - "~>"
74
88
  - !ruby/object:Gem::Version
75
- version: 3.5.0
89
+ version: 3.6.0
76
90
  type: :development
77
91
  prerelease: false
78
92
  version_requirements: !ruby/object:Gem::Requirement
79
93
  requirements:
80
94
  - - "~>"
81
95
  - !ruby/object:Gem::Version
82
- version: 3.5.0
96
+ version: 3.6.0
83
97
  - !ruby/object:Gem::Dependency
84
98
  name: fuubar
85
99
  requirement: !ruby/object:Gem::Requirement
86
100
  requirements:
87
101
  - - "~>"
88
102
  - !ruby/object:Gem::Version
89
- version: 2.0.0
103
+ version: 2.2.0
90
104
  type: :development
91
105
  prerelease: false
92
106
  version_requirements: !ruby/object:Gem::Requirement
93
107
  requirements:
94
108
  - - "~>"
95
109
  - !ruby/object:Gem::Version
96
- version: 2.0.0
110
+ version: 2.2.0
97
111
  - !ruby/object:Gem::Dependency
98
112
  name: webmock
99
113
  requirement: !ruby/object:Gem::Requirement
100
114
  requirements:
101
115
  - - "~>"
102
116
  - !ruby/object:Gem::Version
103
- version: 2.1.0
117
+ version: 3.0.0
104
118
  type: :development
105
119
  prerelease: false
106
120
  version_requirements: !ruby/object:Gem::Requirement
107
121
  requirements:
108
122
  - - "~>"
109
123
  - !ruby/object:Gem::Version
110
- version: 2.1.0
124
+ version: 3.0.0
111
125
  - !ruby/object:Gem::Dependency
112
126
  name: vcr
113
127
  requirement: !ruby/object:Gem::Requirement
@@ -128,6 +142,7 @@ executables: []
128
142
  extensions: []
129
143
  extra_rdoc_files: []
130
144
  files:
145
+ - ".circle-ruby"
131
146
  - ".gitignore"
132
147
  - ".prospectus"
133
148
  - ".rspec"
@@ -142,7 +157,9 @@ files:
142
157
  - dev/spec.yml
143
158
  - dev/version
144
159
  - lib/linodeapi.rb
160
+ - lib/linodeapi/errors.rb
145
161
  - lib/linodeapi/raw.rb
162
+ - lib/linodeapi/retryable.rb
146
163
  - linodeapi.gemspec
147
164
  - spec/fixtures/cassettes/basic_auth.yml
148
165
  - spec/fixtures/cassettes/basic_auth_fail.yml
@@ -173,7 +190,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
173
190
  version: '0'
174
191
  requirements: []
175
192
  rubyforge_project:
176
- rubygems_version: 2.5.1
193
+ rubygems_version: 2.6.13
177
194
  signing_key:
178
195
  specification_version: 4
179
196
  summary: Linode API wrapper