-
Notifications
You must be signed in to change notification settings - Fork 288
Expand file tree
/
Copy pathconnection.rb
More file actions
104 lines (95 loc) · 3.34 KB
/
connection.rb
File metadata and controls
104 lines (95 loc) · 3.34 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# This class represents a Connection to a Docker server. The Connection is
# immutable in that once the url and options is set they cannot be changed.
class Docker::Connection
include Docker::Error
attr_reader :url, :options
# Create a new Connection. This method takes a url (String) and options
# (Hash). These are passed to Excon, so any options valid for `Excon.new`
# can be passed here.
def initialize(url, opts)
case
when !url.is_a?(String)
raise ArgumentError, "Expected a String, got: '#{url}'"
when !opts.is_a?(Hash)
raise ArgumentError, "Expected a Hash, got: '#{opts}'"
else
uri = URI.parse(url)
if uri.scheme == "unix"
@url, @options = 'unix:///', {:socket => uri.path}.merge(opts)
elsif uri.scheme =~ /^(https?|tcp)$/
@url, @options = url, opts
else
@url, @options = "http://#{uri}", opts
end
end
end
# The actual client that sends HTTP methods to the Docker server. This value
# is not cached, since doing so may cause socket errors after bad requests.
def resource
Excon.new(url, options)
end
private :resource
# Send a request to the server with the `
def request(*args, &block)
request = compile_request_params(*args, &block)
log_request(request)
resource.request(request).body
rescue Excon::Errors::BadRequest => ex
raise ClientError, ex.response.body
rescue Excon::Errors::Unauthorized => ex
raise UnauthorizedError, ex.response.body
rescue Excon::Errors::NotFound => ex
raise NotFoundError, ex.response.body
rescue Excon::Errors::Conflict => ex
raise ConflictError, ex.response.body
rescue Excon::Errors::InternalServerError => ex
raise ServerError, ex.response.body
rescue Excon::Errors::Timeout => ex
raise TimeoutError, ex.message
end
def log_request(request)
if Docker.logger
Docker.logger.debug(
[request[:method], request[:path], request[:query], request[:body]]
)
end
end
# Delegate all HTTP methods to the #request.
[:get, :put, :post, :delete].each do |method|
define_method(method) { |*args, &block| request(method, *args, &block) }
end
def to_s
"Docker::Connection { :url => #{url}, :options => #{options} }"
end
private
# Given an HTTP method, path, optional query, extra options, and block,
# compiles a request.
def compile_request_params(http_method, path, query = nil, opts = nil, &block)
query ||= {}
opts ||= {}
headers = opts.delete(:headers) || {}
content_type = opts[:body].nil? ? 'text/plain' : 'application/json'
user_agent = "Swipely/Docker-API #{Docker::VERSION}"
{
:method => http_method,
:path => "/v#{Docker::API_VERSION}#{path}",
:query => compile_nested_request_hash(query),
:headers => { 'Content-Type' => content_type,
'User-Agent' => user_agent,
}.merge(headers),
:expects => (200..204).to_a << 301 << 304,
:idempotent => http_method == :get,
:request_block => block,
}.merge(opts).reject { |_, v| v.nil? }
end
def compile_nested_request_hash(query = nil)
return query unless query.respond_to?(:each_with_object)
query.each_with_object({}) do |(k, v), h|
h[k] = if v.is_a?(Hash)
MultiJson.dump(v)
else
v
end
end
end
end