rancher.rb 0.1.0
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 +7 -0
- data/.codeclimate.yml +42 -0
- data/.gitignore +14 -0
- data/.ruby-version +1 -0
- data/.travis.yml +16 -0
- data/Gemfile +22 -0
- data/LICENSE.txt +22 -0
- data/README.md +70 -0
- data/Rakefile +6 -0
- data/bin/console +15 -0
- data/bin/setup +7 -0
- data/lib/rancher.rb +32 -0
- data/lib/rancher/arguments.rb +12 -0
- data/lib/rancher/authentication.rb +8 -0
- data/lib/rancher/classify.rb +66 -0
- data/lib/rancher/client.rb +81 -0
- data/lib/rancher/collection.rb +42 -0
- data/lib/rancher/configurable.rb +84 -0
- data/lib/rancher/connection.rb +192 -0
- data/lib/rancher/default.rb +109 -0
- data/lib/rancher/error.rb +241 -0
- data/lib/rancher/middleware/follow_redirects.rb +131 -0
- data/lib/rancher/resource.rb +94 -0
- data/lib/rancher/response/raise_error.rb +21 -0
- data/lib/rancher/type.rb +112 -0
- data/lib/rancher/version.rb +18 -0
- data/rancher.gemspec +23 -0
- data/spec/cassettes/Rancher_Client/_get/handles_headers.json +1 -0
- data/spec/cassettes/Rancher_Client/_head/handles_headers.json +1 -0
- data/spec/cassettes/Rancher_Client/_last_response/caches_the_last_agent_response.json +1 -0
- data/spec/cassettes/root.json +1 -0
- data/spec/helper.rb +120 -0
- data/spec/rancher/client_spec.rb +187 -0
- data/spec/rancher/type_spec.rb +135 -0
- data/spec/rancher_spec.rb +46 -0
- metadata +122 -0
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'rancher/resource'
|
2
|
+
module Rancher
|
3
|
+
# A Collection of Resources
|
4
|
+
class Collection < Resource
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
def initialize(data)
|
8
|
+
@data = data[:data] if data.key?(:data)
|
9
|
+
super(data)
|
10
|
+
end
|
11
|
+
|
12
|
+
def create(attrs)
|
13
|
+
attrs = attrs.meta if attrs.is_a?(Rancher::Resource)
|
14
|
+
|
15
|
+
Rancher.post get_link('self'), attrs
|
16
|
+
end
|
17
|
+
|
18
|
+
def remove!(id_or_obj)
|
19
|
+
id = id_or_obj.get_id if id_or_obj.is_a?(Rancher::Resource)
|
20
|
+
link = get_link('self') + "/#{id}"
|
21
|
+
|
22
|
+
Rancher.delete link
|
23
|
+
end
|
24
|
+
|
25
|
+
def each
|
26
|
+
return @data.enum_for(:each) unless block_given?
|
27
|
+
|
28
|
+
@data.each { |d| yield d }
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def schema_field(name)
|
34
|
+
type_name = get_type
|
35
|
+
type = Rancher.types[type_name.to_sym]
|
36
|
+
|
37
|
+
type.collection_field(name) if type
|
38
|
+
|
39
|
+
type
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module Rancher
|
2
|
+
# Configuration options for {Client}, defaulting to values
|
3
|
+
# in {Default}
|
4
|
+
module Configurable
|
5
|
+
# @!attribute api_endpoint
|
6
|
+
# @return [String] Base URL for API requests. default: http://localhost:8080/v1/projects/1p1
|
7
|
+
# @!attribute access_key
|
8
|
+
# @return [String] Access Key from inside rancher
|
9
|
+
# @!attribute [w] secret_key
|
10
|
+
# @return [String] Secrete Key form inside rancher
|
11
|
+
# @!attribute connection_options
|
12
|
+
# @see https://github.com/lostisland/faraday
|
13
|
+
# @return [Hash] Configure connection options for Faraday
|
14
|
+
# @!attribute middleware
|
15
|
+
# @see https://Rancher.com/lostisland/faraday
|
16
|
+
# @return [Faraday::Builder or Faraday::RackBuilder] Configure middleware for Faraday
|
17
|
+
# @!attribute proxy
|
18
|
+
# @see https://Rancher.com/lostisland/faraday
|
19
|
+
# @return [String] URI for proxy server
|
20
|
+
# @!attribute user_agent
|
21
|
+
# @return [String] Configure User-Agent header for requests.
|
22
|
+
|
23
|
+
attr_accessor :access_key, :secret_key, :connection_options,
|
24
|
+
:middleware, :proxy, :user_agent, :default_media_type
|
25
|
+
attr_writer :api_endpoint
|
26
|
+
|
27
|
+
class << self
|
28
|
+
# List of configurable keys for {Rancher::Client}
|
29
|
+
# @return [Array] of option keys
|
30
|
+
def keys
|
31
|
+
@keys ||= [
|
32
|
+
:api_endpoint,
|
33
|
+
:access_key,
|
34
|
+
:secret_key,
|
35
|
+
:connection_options,
|
36
|
+
:default_media_type,
|
37
|
+
:middleware,
|
38
|
+
:proxy,
|
39
|
+
:user_agent
|
40
|
+
]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Set configuration options using a block
|
45
|
+
def configure
|
46
|
+
yield self
|
47
|
+
end
|
48
|
+
|
49
|
+
# Reset configuration options to default values
|
50
|
+
def reset!
|
51
|
+
Rancher::Configurable.keys.each do |key|
|
52
|
+
instance_variable_set(:"@#{key}", Rancher::Default.options[key])
|
53
|
+
end
|
54
|
+
self
|
55
|
+
end
|
56
|
+
alias_method :setup, :reset!
|
57
|
+
|
58
|
+
# Compares client options to a Hash of requested options
|
59
|
+
#
|
60
|
+
# @param opts [Hash] Options to compare with current client options
|
61
|
+
# @return [Boolean]
|
62
|
+
def same_options?(opts)
|
63
|
+
opts.hash == options.hash
|
64
|
+
end
|
65
|
+
|
66
|
+
def api_endpoint
|
67
|
+
File.join(@api_endpoint, '')
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def options
|
73
|
+
Hash[Rancher::Configurable.keys.map do |key|
|
74
|
+
[key, instance_variable_get(:"@#{key}")]
|
75
|
+
end
|
76
|
+
]
|
77
|
+
end
|
78
|
+
|
79
|
+
def fetch_access_key_and_secret(overrides = {})
|
80
|
+
opts = options.merge(overrides)
|
81
|
+
opts.values_at :access_key, :secret_key
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,192 @@
|
|
1
|
+
require 'sawyer'
|
2
|
+
require 'rancher/authentication'
|
3
|
+
require 'rancher/classify'
|
4
|
+
module Rancher
|
5
|
+
|
6
|
+
# Network layer for API clients.
|
7
|
+
module Connection
|
8
|
+
|
9
|
+
include Rancher::Authentication
|
10
|
+
include Rancher::Classify
|
11
|
+
|
12
|
+
# Header keys that can be passed in options hash to {#get},{#head}
|
13
|
+
CONVENIENCE_HEADERS = Set.new([:accept, :content_type])
|
14
|
+
|
15
|
+
# Make a HTTP GET request
|
16
|
+
#
|
17
|
+
# @param url [String] The path, relative to {#api_endpoint}
|
18
|
+
# @param options [Hash] Query and header params for request
|
19
|
+
# @return [Sawyer::Resource]
|
20
|
+
def get(url, options = {})
|
21
|
+
request :get, url, parse_query_and_convenience_headers(options)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Make a HTTP POST request
|
25
|
+
#
|
26
|
+
# @param url [String] The path, relative to {#api_endpoint}
|
27
|
+
# @param options [Hash] Body and header params for request
|
28
|
+
# @return [Sawyer::Resource]
|
29
|
+
def post(url, options = {})
|
30
|
+
request :post, url, options
|
31
|
+
end
|
32
|
+
|
33
|
+
# Make a HTTP PUT request
|
34
|
+
#
|
35
|
+
# @param url [String] The path, relative to {#api_endpoint}
|
36
|
+
# @param options [Hash] Body and header params for request
|
37
|
+
# @return [Sawyer::Resource]
|
38
|
+
def put(url, options = {})
|
39
|
+
request :put, url, options
|
40
|
+
end
|
41
|
+
|
42
|
+
# Make a HTTP PATCH request
|
43
|
+
#
|
44
|
+
# @param url [String] The path, relative to {#api_endpoint}
|
45
|
+
# @param options [Hash] Body and header params for request
|
46
|
+
# @return [Sawyer::Resource]
|
47
|
+
def patch(url, options = {})
|
48
|
+
request :patch, url, options
|
49
|
+
end
|
50
|
+
|
51
|
+
# Make a HTTP DELETE request
|
52
|
+
#
|
53
|
+
# @param url [String] The path, relative to {#api_endpoint}
|
54
|
+
# @param options [Hash] Query and header params for request
|
55
|
+
# @return [Sawyer::Resource]
|
56
|
+
def delete(url, options = {})
|
57
|
+
request :delete, url, options
|
58
|
+
end
|
59
|
+
|
60
|
+
# Make a HTTP HEAD request
|
61
|
+
#
|
62
|
+
# @param url [String] The path, relative to {#api_endpoint}
|
63
|
+
# @param options [Hash] Query and header params for request
|
64
|
+
# @return [Sawyer::Resource]
|
65
|
+
def head(url, options = {})
|
66
|
+
request :head, url, parse_query_and_convenience_headers(options)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Make one or more HTTP GET requests, optionally fetching
|
70
|
+
# the next page of results from URL in Link response header based
|
71
|
+
# on value in {#auto_paginate}.
|
72
|
+
#
|
73
|
+
# @param url [String] The path, relative to {#api_endpoint}
|
74
|
+
# @param options [Hash] Query and header params for request
|
75
|
+
# @param block [Block] Block to perform the data concatination of the
|
76
|
+
# multiple requests. The block is called with two parameters, the first
|
77
|
+
# contains the contents of the requests so far and the second parameter
|
78
|
+
# contains the latest response.
|
79
|
+
# @return [Sawyer::Resource]
|
80
|
+
def paginate(url, options = {}, &block)
|
81
|
+
opts = parse_query_and_convenience_headers(options.dup)
|
82
|
+
if @auto_paginate || @per_page
|
83
|
+
opts[:query][:per_page] ||= @per_page || (@auto_paginate ? 100 : nil)
|
84
|
+
end
|
85
|
+
|
86
|
+
data = request(:get, url, opts.dup)
|
87
|
+
|
88
|
+
if @auto_paginate
|
89
|
+
while @last_response.rels[:next] && rate_limit.remaining > 0
|
90
|
+
@last_response = @last_response.rels[:next].get(:headers => opts[:headers])
|
91
|
+
if block_given?
|
92
|
+
yield(data, @last_response)
|
93
|
+
else
|
94
|
+
data.concat(@last_response.data) if @last_response.data.is_a?(Array)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
data
|
101
|
+
end
|
102
|
+
|
103
|
+
# Hypermedia agent for the Rancher API
|
104
|
+
#
|
105
|
+
# @return [Sawyer::Agent]
|
106
|
+
def agent
|
107
|
+
@agent ||= Sawyer::Agent.new(endpoint, sawyer_options) do |http|
|
108
|
+
http.headers[:accept] = default_media_type
|
109
|
+
http.headers[:content_type] = "application/json"
|
110
|
+
http.headers[:user_agent] = user_agent
|
111
|
+
if basic_authenticated?
|
112
|
+
http.basic_auth(@access_key, @secret_key)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# Fetch the root resource for the API
|
118
|
+
#
|
119
|
+
# @return [Sawyer::Resource]
|
120
|
+
def root
|
121
|
+
get ""
|
122
|
+
end
|
123
|
+
|
124
|
+
# Response for last HTTP request
|
125
|
+
#
|
126
|
+
# @return [Sawyer::Response]
|
127
|
+
def last_response
|
128
|
+
@last_response if defined? @last_response
|
129
|
+
end
|
130
|
+
|
131
|
+
protected
|
132
|
+
|
133
|
+
def endpoint
|
134
|
+
api_endpoint
|
135
|
+
end
|
136
|
+
|
137
|
+
private
|
138
|
+
|
139
|
+
def reset_agent
|
140
|
+
@agent = nil
|
141
|
+
end
|
142
|
+
|
143
|
+
def request(method, path, data, options = {})
|
144
|
+
if data.is_a?(Hash)
|
145
|
+
options[:query] = data.delete(:query) || {}
|
146
|
+
options[:headers] = data.delete(:headers) || {}
|
147
|
+
if accept = data.delete(:accept)
|
148
|
+
options[:headers][:accept] = accept
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
@last_response = response = agent.call(method, URI::Parser.new.escape(path.to_s), data, options)
|
153
|
+
classify response.data
|
154
|
+
end
|
155
|
+
|
156
|
+
# Executes the request, checking if it was successful
|
157
|
+
#
|
158
|
+
# @return [Boolean] True on success, false otherwise
|
159
|
+
def boolean_from_response(method, path, options = {})
|
160
|
+
request(method, path, options)
|
161
|
+
@last_response.status == 204
|
162
|
+
rescue Rancher::NotFound
|
163
|
+
false
|
164
|
+
end
|
165
|
+
|
166
|
+
|
167
|
+
def sawyer_options
|
168
|
+
opts = {}
|
169
|
+
conn_opts = @connection_options
|
170
|
+
conn_opts[:builder] = @middleware if @middleware
|
171
|
+
conn_opts[:proxy] = @proxy if @proxy
|
172
|
+
opts[:faraday] = Faraday.new(conn_opts)
|
173
|
+
|
174
|
+
opts
|
175
|
+
end
|
176
|
+
|
177
|
+
def parse_query_and_convenience_headers(options)
|
178
|
+
headers = options.delete(:headers) { Hash.new }
|
179
|
+
CONVENIENCE_HEADERS.each do |h|
|
180
|
+
if header = options.delete(h)
|
181
|
+
headers[h] = header
|
182
|
+
end
|
183
|
+
end
|
184
|
+
query = options.delete(:query)
|
185
|
+
opts = {:query => options}
|
186
|
+
opts[:query].merge!(query) if query && query.is_a?(Hash)
|
187
|
+
opts[:headers] = headers unless headers.empty?
|
188
|
+
|
189
|
+
opts
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'rancher/middleware/follow_redirects'
|
2
|
+
require 'rancher/response/raise_error'
|
3
|
+
require 'rancher/version'
|
4
|
+
|
5
|
+
module Rancher
|
6
|
+
# Default configuration options for {Client}
|
7
|
+
module Default
|
8
|
+
# Default API endpoint
|
9
|
+
API_ENDPOINT = 'http://localhost:8080/v1/projects/1a5'.freeze
|
10
|
+
|
11
|
+
# Default User Agent header string
|
12
|
+
USER_AGENT = "Rancher Ruby Gem #{Rancher::VERSION}".freeze
|
13
|
+
|
14
|
+
# Default media type
|
15
|
+
MEDIA_TYPE = 'application/json'.freeze
|
16
|
+
|
17
|
+
# In Faraday 0.9, Faraday::Builder was renamed to Faraday::RackBuilder
|
18
|
+
RACK_BUILDER_CLASS = defined?(Faraday::RackBuilder) ? Faraday::RackBuilder : Faraday::Builder
|
19
|
+
|
20
|
+
# Default Faraday middleware stack
|
21
|
+
MIDDLEWARE = RACK_BUILDER_CLASS.new do |builder|
|
22
|
+
builder.use Rancher::Middleware::FollowRedirects
|
23
|
+
builder.use Rancher::Response::RaiseError
|
24
|
+
builder.adapter Faraday.default_adapter
|
25
|
+
end
|
26
|
+
|
27
|
+
class << self
|
28
|
+
# Configuration options
|
29
|
+
# @return [Hash]
|
30
|
+
def options
|
31
|
+
Hash[Rancher::Configurable.keys.map { |key| [key, send(key)] }]
|
32
|
+
end
|
33
|
+
|
34
|
+
# Default API endpoint from ENV or {API_ENDPOINT}
|
35
|
+
# @return [String]
|
36
|
+
def api_endpoint
|
37
|
+
ENV['RANCHER_API_ENDPOINT'] || API_ENDPOINT
|
38
|
+
end
|
39
|
+
|
40
|
+
# Default pagination preference from ENV
|
41
|
+
# @return [String]
|
42
|
+
def auto_paginate
|
43
|
+
ENV['RANCHER_AUTO_PAGINATE']
|
44
|
+
end
|
45
|
+
|
46
|
+
# Default OAuth app key from ENV
|
47
|
+
# @return [String]
|
48
|
+
def access_key
|
49
|
+
ENV['RANCHER_CLIENT_ID']
|
50
|
+
end
|
51
|
+
|
52
|
+
# Default OAuth app secret from ENV
|
53
|
+
# @return [String]
|
54
|
+
def secret_key
|
55
|
+
ENV['RANCHER_SECRET']
|
56
|
+
end
|
57
|
+
|
58
|
+
# Default options for Faraday::Connection
|
59
|
+
# @return [Hash]
|
60
|
+
def connection_options
|
61
|
+
{
|
62
|
+
headers: {
|
63
|
+
accept: default_media_type,
|
64
|
+
user_agent: user_agent
|
65
|
+
}
|
66
|
+
}
|
67
|
+
end
|
68
|
+
|
69
|
+
# Default media type from ENV or {MEDIA_TYPE}
|
70
|
+
# @return [String]
|
71
|
+
def default_media_type
|
72
|
+
ENV['RANCHER_DEFAULT_MEDIA_TYPE'] || MEDIA_TYPE
|
73
|
+
end
|
74
|
+
|
75
|
+
# Default middleware stack for Faraday::Connection
|
76
|
+
# from {MIDDLEWARE}
|
77
|
+
# @return [String]
|
78
|
+
def middleware
|
79
|
+
MIDDLEWARE
|
80
|
+
end
|
81
|
+
|
82
|
+
# Default pagination page size from ENV
|
83
|
+
# @return [Fixnum] Page size
|
84
|
+
def per_page
|
85
|
+
page_size = ENV['RANCHER_PER_PAGE']
|
86
|
+
|
87
|
+
page_size.to_i if page_size
|
88
|
+
end
|
89
|
+
|
90
|
+
# Default proxy server URI for Faraday connection from ENV
|
91
|
+
# @return [String]
|
92
|
+
def proxy
|
93
|
+
ENV['RANCHER_PROXY']
|
94
|
+
end
|
95
|
+
|
96
|
+
# Default User-Agent header string from ENV or {USER_AGENT}
|
97
|
+
# @return [String]
|
98
|
+
def user_agent
|
99
|
+
ENV['RANCHER_USER_AGENT'] || USER_AGENT
|
100
|
+
end
|
101
|
+
|
102
|
+
# Default web endpoint from ENV or {WEB_ENDPOINT}
|
103
|
+
# @return [String]
|
104
|
+
def web_endpoint
|
105
|
+
ENV['RANCHER_WEB_ENDPOINT'] || WEB_ENDPOINT
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,241 @@
|
|
1
|
+
module Rancher
|
2
|
+
# Custom error class for rescuing from all Rancher errors
|
3
|
+
class Error < StandardError
|
4
|
+
|
5
|
+
# Returns the appropriate Rancher::Error subclass based
|
6
|
+
# on status and response message
|
7
|
+
#
|
8
|
+
# @param [Hash] response HTTP response
|
9
|
+
# @return [Rancher::Error]
|
10
|
+
def self.from_response(response)
|
11
|
+
status = response[:status].to_i
|
12
|
+
body = response[:body].to_s
|
13
|
+
headers = response[:response_headers]
|
14
|
+
|
15
|
+
if klass = case status
|
16
|
+
when 400 then Rancher::BadRequest
|
17
|
+
when 401 then error_for_401(headers)
|
18
|
+
when 403 then error_for_403(body)
|
19
|
+
when 404 then Rancher::NotFound
|
20
|
+
when 405 then Rancher::MethodNotAllowed
|
21
|
+
when 406 then Rancher::NotAcceptable
|
22
|
+
when 409 then Rancher::Conflict
|
23
|
+
when 415 then Rancher::UnsupportedMediaType
|
24
|
+
when 422 then Rancher::UnprocessableEntity
|
25
|
+
when 400..499 then Rancher::ClientError
|
26
|
+
when 500 then Rancher::InternalServerError
|
27
|
+
when 501 then Rancher::NotImplemented
|
28
|
+
when 502 then Rancher::BadGateway
|
29
|
+
when 503 then Rancher::ServiceUnavailable
|
30
|
+
when 500..599 then Rancher::ServerError
|
31
|
+
end
|
32
|
+
klass.new(response)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def initialize(response=nil)
|
37
|
+
@response = response
|
38
|
+
super(build_error_message)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Documentation URL returned by the API for some errors
|
42
|
+
#
|
43
|
+
# @return [String]
|
44
|
+
def documentation_url
|
45
|
+
data[:documentation_url] if data.is_a? Hash
|
46
|
+
end
|
47
|
+
|
48
|
+
# Returns most appropriate error for 401 HTTP status code
|
49
|
+
# @private
|
50
|
+
def self.error_for_401(headers)
|
51
|
+
if Rancher::OneTimePasswordRequired.required_header(headers)
|
52
|
+
Rancher::OneTimePasswordRequired
|
53
|
+
else
|
54
|
+
Rancher::Unauthorized
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Returns most appropriate error for 403 HTTP status code
|
59
|
+
# @private
|
60
|
+
def self.error_for_403(body)
|
61
|
+
if body =~ /rate limit exceeded/i
|
62
|
+
Rancher::TooManyRequests
|
63
|
+
elsif body =~ /login attempts exceeded/i
|
64
|
+
Rancher::TooManyLoginAttempts
|
65
|
+
elsif body =~ /abuse/i
|
66
|
+
Rancher::AbuseDetected
|
67
|
+
elsif body =~ /repository access blocked/i
|
68
|
+
Rancher::RepositoryUnavailable
|
69
|
+
else
|
70
|
+
Rancher::Forbidden
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# Array of validation errors
|
75
|
+
# @return [Array<Hash>] Error info
|
76
|
+
def errors
|
77
|
+
if data && data.is_a?(Hash)
|
78
|
+
data[:errors] || []
|
79
|
+
else
|
80
|
+
[]
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def data
|
87
|
+
@data ||=
|
88
|
+
if (body = @response[:body]) && !body.empty?
|
89
|
+
if body.is_a?(String) &&
|
90
|
+
@response[:response_headers] &&
|
91
|
+
@response[:response_headers][:content_type] =~ /json/
|
92
|
+
|
93
|
+
Sawyer::Agent.serializer.decode(body)
|
94
|
+
else
|
95
|
+
body
|
96
|
+
end
|
97
|
+
else
|
98
|
+
nil
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def response_message
|
103
|
+
case data
|
104
|
+
when Hash
|
105
|
+
data[:message]
|
106
|
+
when String
|
107
|
+
data
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def response_error
|
112
|
+
"Error: #{data[:error]}" if data.is_a?(Hash) && data[:error]
|
113
|
+
end
|
114
|
+
|
115
|
+
def response_error_summary
|
116
|
+
return nil unless data.is_a?(Hash) && !Array(data[:errors]).empty?
|
117
|
+
|
118
|
+
summary = "\nError summary:\n"
|
119
|
+
summary << data[:errors].map do |hash|
|
120
|
+
hash.map { |k,v| " #{k}: #{v}" }
|
121
|
+
end.join("\n")
|
122
|
+
|
123
|
+
summary
|
124
|
+
end
|
125
|
+
|
126
|
+
def build_error_message
|
127
|
+
return nil if @response.nil?
|
128
|
+
|
129
|
+
message = "#{@response[:method].to_s.upcase} "
|
130
|
+
message << redact_url(@response[:url].to_s) + ": "
|
131
|
+
message << "#{@response[:status]} - "
|
132
|
+
message << "#{response_message}" unless response_message.nil?
|
133
|
+
message << "#{response_error}" unless response_error.nil?
|
134
|
+
message << "#{response_error_summary}" unless response_error_summary.nil?
|
135
|
+
message << " // See: #{documentation_url}" unless documentation_url.nil?
|
136
|
+
message
|
137
|
+
end
|
138
|
+
|
139
|
+
def redact_url(url_string)
|
140
|
+
%w[secret_key access_token].each do |token|
|
141
|
+
url_string.gsub!(/#{token}=\S+/, "#{token}=(redacted)") if url_string.include? token
|
142
|
+
end
|
143
|
+
url_string
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# Raised on errors in the 400-499 range
|
148
|
+
class ClientError < Error; end
|
149
|
+
|
150
|
+
# Raised when Rancher returns a 400 HTTP status code
|
151
|
+
class BadRequest < ClientError; end
|
152
|
+
|
153
|
+
# Raised when Rancher returns a 401 HTTP status code
|
154
|
+
class Unauthorized < ClientError; end
|
155
|
+
|
156
|
+
# Raised when Rancher returns a 401 HTTP status code
|
157
|
+
# and headers include "X-Rancher-OTP"
|
158
|
+
class OneTimePasswordRequired < ClientError
|
159
|
+
#@private
|
160
|
+
OTP_DELIVERY_PATTERN = /required; (\w+)/i
|
161
|
+
|
162
|
+
#@private
|
163
|
+
def self.required_header(headers)
|
164
|
+
OTP_DELIVERY_PATTERN.match headers['X-Rancher-OTP'].to_s
|
165
|
+
end
|
166
|
+
|
167
|
+
# Delivery method for the user's OTP
|
168
|
+
#
|
169
|
+
# @return [String]
|
170
|
+
def password_delivery
|
171
|
+
@password_delivery ||= delivery_method_from_header
|
172
|
+
end
|
173
|
+
|
174
|
+
private
|
175
|
+
|
176
|
+
def delivery_method_from_header
|
177
|
+
if match = self.class.required_header(@response[:response_headers])
|
178
|
+
match[1]
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
# Raised when Rancher returns a 403 HTTP status code
|
184
|
+
class Forbidden < ClientError; end
|
185
|
+
|
186
|
+
# Raised when Rancher returns a 403 HTTP status code
|
187
|
+
# and body matches 'rate limit exceeded'
|
188
|
+
class TooManyRequests < Forbidden; end
|
189
|
+
|
190
|
+
# Raised when Rancher returns a 403 HTTP status code
|
191
|
+
# and body matches 'login attempts exceeded'
|
192
|
+
class TooManyLoginAttempts < Forbidden; end
|
193
|
+
|
194
|
+
# Raised when Rancher returns a 403 HTTP status code
|
195
|
+
# and body matches 'abuse'
|
196
|
+
class AbuseDetected < Forbidden; end
|
197
|
+
|
198
|
+
# Raised when Rancher returns a 403 HTTP status code
|
199
|
+
# and body matches 'repository access blocked'
|
200
|
+
class RepositoryUnavailable < Forbidden; end
|
201
|
+
|
202
|
+
# Raised when Rancher returns a 404 HTTP status code
|
203
|
+
class NotFound < ClientError; end
|
204
|
+
|
205
|
+
# Raised when Rancher returns a 405 HTTP status code
|
206
|
+
class MethodNotAllowed < ClientError; end
|
207
|
+
|
208
|
+
# Raised when Rancher returns a 406 HTTP status code
|
209
|
+
class NotAcceptable < ClientError; end
|
210
|
+
|
211
|
+
# Raised when Rancher returns a 409 HTTP status code
|
212
|
+
class Conflict < ClientError; end
|
213
|
+
|
214
|
+
# Raised when Rancher returns a 414 HTTP status code
|
215
|
+
class UnsupportedMediaType < ClientError; end
|
216
|
+
|
217
|
+
# Raised when Rancher returns a 422 HTTP status code
|
218
|
+
class UnprocessableEntity < ClientError; end
|
219
|
+
|
220
|
+
# Raised on errors in the 500-599 range
|
221
|
+
class ServerError < Error; end
|
222
|
+
|
223
|
+
# Raised when Rancher returns a 500 HTTP status code
|
224
|
+
class InternalServerError < ServerError; end
|
225
|
+
|
226
|
+
# Raised when Rancher returns a 501 HTTP status code
|
227
|
+
class NotImplemented < ServerError; end
|
228
|
+
|
229
|
+
# Raised when Rancher returns a 502 HTTP status code
|
230
|
+
class BadGateway < ServerError; end
|
231
|
+
|
232
|
+
# Raised when Rancher returns a 503 HTTP status code
|
233
|
+
class ServiceUnavailable < ServerError; end
|
234
|
+
|
235
|
+
# Raised when client fails to provide valid Content-Type
|
236
|
+
class MissingContentType < ArgumentError; end
|
237
|
+
|
238
|
+
# Raised when a method requires an application access_key
|
239
|
+
# and secret but none is provided
|
240
|
+
class ApplicationCredentialsRequired < StandardError; end
|
241
|
+
end
|