docker-api 1.0.1 → 1.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.
- data/README.md +2 -0
- data/lib/docker.rb +12 -2
- data/lib/docker/connection.rb +42 -49
- data/lib/docker/container.rb +12 -20
- data/lib/docker/error.rb +3 -0
- data/lib/docker/image.rb +21 -47
- data/lib/docker/model.rb +17 -14
- data/lib/docker/util.rb +12 -0
- data/lib/docker/version.rb +5 -1
- data/spec/docker/connection_spec.rb +56 -30
- data/spec/docker/container_spec.rb +81 -201
- data/spec/docker/image_spec.rb +48 -157
- data/spec/docker/util_spec.rb +43 -0
- data/spec/docker_spec.rb +15 -0
- data/spec/vcr/Docker/_info/returns_the_info_as_a_Hash.yml +10 -8
- data/spec/vcr/Docker/_validate_version/when_nothing_is_raised/validate_version_/.yml +33 -0
- data/spec/vcr/Docker/_version/returns_the_version_as_a_Hash.yml +10 -8
- data/spec/vcr/Docker_Container/_all/when_the_HTTP_response_is_a_200/materializes_each_Container_into_a_Docker_Container.yml +857 -17
- data/spec/vcr/Docker_Container/_attach/{when_the_HTTP_response_status_is_200/yields_each_chunk.yml → yields_each_chunk.yml} +9 -9
- data/spec/vcr/Docker_Container/_changes/{when_the_HTTP_response_status_is_200/returns_the_changes_as_an_array.yml → returns_the_changes_as_an_array.yml} +13 -13
- data/spec/vcr/Docker_Container/_commit/{when_the_HTTP_response_status_is_200/creates_a_new_Image_from_the_Container_s_changes.yml → creates_a_new_Image_from_the_Container_s_changes.yml} +12 -12
- data/spec/vcr/Docker_Container/_create/when_the_Container_does_not_yet_exist_and_the_body_is_a_Hash/when_the_HTTP_request_returns_a_200/sets_the_id.yml +4 -4
- data/spec/vcr/Docker_Container/_export/{when_the_HTTP_response_status_is_200/yields_each_chunk.yml → yields_each_chunk.yml} +11 -11
- data/spec/vcr/Docker_Container/_json/{when_the_HTTP_response_status_is_200/returns_the_description_as_a_Hash.yml → returns_the_description_as_a_Hash.yml} +8 -8
- data/spec/vcr/Docker_Container/_kill/{when_the_HTTP_response_status_is_204/kills_the_container.yml → kills_the_container.yml} +436 -16
- data/spec/vcr/Docker_Container/_restart/{when_the_HTTP_response_status_is_204/restarts_the_container.yml → restarts_the_container.yml} +29 -29
- data/spec/vcr/Docker_Container/_start/{when_the_HTTP_response_status_is_200/starts_the_container.yml → starts_the_container.yml} +12 -12
- data/spec/vcr/Docker_Container/_stop/{when_the_HTTP_response_status_is_204/stops_the_container.yml → stops_the_container.yml} +440 -20
- data/spec/vcr/Docker_Container/_wait/{when_the_HTTP_response_status_is_200/waits_for_the_command_to_finish.yml → waits_for_the_command_to_finish.yml} +10 -10
- data/spec/vcr/Docker_Image/_all/materializes_each_Image_into_a_Docker_Image.yml +65 -0
- data/spec/vcr/Docker_Image/_build/with_a_valid_Dockerfile/builds_an_image.yml +9 -5
- data/spec/vcr/Docker_Image/_build/with_an_invalid_Dockerfile/throws_a_UnexpectedResponseError.yml +9 -5
- data/spec/vcr/Docker_Image/_build_from_dir/with_a_valid_Dockerfile/builds_the_image.yml +17 -15
- data/spec/vcr/Docker_Image/{_create_/when_the_Image_does_not_yet_exist_and_the_body_is_a_Hash/when_the_HTTP_request_returns_a_200 → _create/when_the_Image_does_not_yet_exist_and_the_body_is_a_Hash}/sets_the_id.yml +4 -2
- data/spec/vcr/Docker_Image/_history/{when_the_Image_has_been_created/when_the_HTTP_response_status_is_200/returns_the_history_of_the_Image.yml → returns_the_history_of_the_Image.yml} +7 -5
- data/spec/vcr/Docker_Image/_import/when_the_file_does_exist/creates_the_Image.yml +6 -6
- data/spec/vcr/Docker_Image/_insert/{when_the_Image_has_been_created/when_the_HTTP_response_status_is_200/inserts_the_url_s_file_into_a_new_Image.yml → inserts_the_url_s_file_into_a_new_Image.yml} +30 -29
- data/spec/vcr/Docker_Image/_json/{when_the_Image_has_been_created/when_the_HTTP_response_status_is_200/returns_additional_information_about_image_image.yml → returns_additional_information_about_image_image.yml} +7 -5
- data/spec/vcr/Docker_Image/_remove/removes_the_Image.yml +95 -0
- data/spec/vcr/Docker_Image/_run/when_the_argument_is_a_String/splits_the_String_by_spaces_and_creates_a_new_Container.yml +14 -12
- data/spec/vcr/Docker_Image/_run/when_the_argument_is_an_Array/creates_a_new_Container.yml +14 -12
- data/spec/vcr/Docker_Image/_search/{when_the_HTTP_response_is_a_200/materializes_each_Image_into_a_Docker_Image.yml → materializes_each_Image_into_a_Docker_Image.yml} +4 -34
- data/spec/vcr/Docker_Image/_tag/{when_the_Image_has_been_created/when_the_HTTP_response_status_is_200/tags_the_image_with_the_repo_name.yml → tags_the_image_with_the_repo_name.yml} +7 -5
- metadata +43 -56
- data/spec/vcr/Docker_Image/_all/when_the_HTTP_response_is_a_200/materializes_each_Image_into_a_Docker_Image.yml +0 -63
- data/spec/vcr/Docker_Image/_create/when_the_Image_does_not_yet_exist_and_the_body_is_a_Hash/when_the_HTTP_request_returns_a_200/sets_the_id.yml +0 -33
- data/spec/vcr/Docker_Image/_history/when_the_HTTP_response_status_is_200/returns_the_history_of_the_Image.yml +0 -63
- data/spec/vcr/Docker_Image/_insert/when_the_HTTP_response_status_is_200/inserts_the_url_s_file_into_a_new_Image.yml +0 -220
- data/spec/vcr/Docker_Image/_json/when_the_HTTP_response_status_is_200/returns_additional_information_about_image_image.yml +0 -63
- data/spec/vcr/Docker_Image/_remove/when_the_HTTP_response_status_is_204/removes_the_Image.yml +0 -93
- data/spec/vcr/Docker_Image/_remove/when_the_Image_has_been_created/when_the_HTTP_response_status_is_204/nils_out_the_id.yml +0 -63
- data/spec/vcr/Docker_Image/_remove/when_the_Image_has_been_created/when_the_HTTP_response_status_is_204/removes_the_Image.yml +0 -63
- data/spec/vcr/Docker_Image/_run/when_the_Image_has_been_created/when_the_argument_is_a_String/splits_the_String_by_spaces_and_creates_a_new_Container.yml +0 -119
- data/spec/vcr/Docker_Image/_run/when_the_Image_has_been_created/when_the_argument_is_an_Array/creates_a_new_Container.yml +0 -119
- data/spec/vcr/Docker_Image/_tag/when_the_HTTP_response_status_is_200/tags_the_image_with_the_repo_name.yml +0 -63
data/README.md
CHANGED
@@ -51,6 +51,8 @@ Docker.options = { :port => 5422 }
|
|
51
51
|
|
52
52
|
Two things to note here. The first is that this gem uses [excon](http://www.github.com/geemus/excon), so any of the options that are valid for `Excon.new` are alse valid for `Docker.options`. Second, by default Docker runs on port 4243. The gem will assume you want to connnect to port 4243 unless you specify otherwise.
|
53
53
|
|
54
|
+
Before doing anything else, ensure you have the correct version of the Docker API. To do this, run `Docker.validate_version!`. If your installed version is not supported, a `Docker::Error::VersionError` is raised.
|
55
|
+
|
54
56
|
## Global calls
|
55
57
|
|
56
58
|
All of the following examples require a connection to a Docker server. See the <a href="#starting-up">Starting up</a> section above for more information.
|
data/lib/docker.rb
CHANGED
@@ -39,12 +39,12 @@ module Docker
|
|
39
39
|
|
40
40
|
# Get the version of Go, Docker, and optionally the Git commit.
|
41
41
|
def version
|
42
|
-
connection.
|
42
|
+
Util.parse_json(connection.request(:get, '/version'))
|
43
43
|
end
|
44
44
|
|
45
45
|
# Get more information about the Docker server.
|
46
46
|
def info
|
47
|
-
connection.
|
47
|
+
Util.parse_json(connection.request(:get, '/info'))
|
48
48
|
end
|
49
49
|
|
50
50
|
# Login to the Docker registry.
|
@@ -53,10 +53,20 @@ module Docker
|
|
53
53
|
connection.post(:path => '/auth', :body => @creds)
|
54
54
|
true
|
55
55
|
end
|
56
|
+
|
57
|
+
# When the correct version of Docker is installed, returns true. Otherwise,
|
58
|
+
# raises a VersionError.
|
59
|
+
def validate_version!
|
60
|
+
Docker.info
|
61
|
+
true
|
62
|
+
rescue Docker::Error::DockerError
|
63
|
+
raise Docker::Error::VersionError, "Expected API Version: #{API_VERSION}"
|
64
|
+
end
|
56
65
|
end
|
57
66
|
|
58
67
|
require 'docker/version'
|
59
68
|
require 'docker/error'
|
69
|
+
require 'docker/util'
|
60
70
|
require 'docker/connection'
|
61
71
|
require 'docker/model'
|
62
72
|
require 'docker/container'
|
data/lib/docker/connection.rb
CHANGED
@@ -5,69 +5,62 @@ class Docker::Connection
|
|
5
5
|
|
6
6
|
attr_reader :url, :options
|
7
7
|
|
8
|
-
# Create a new Connection.
|
9
|
-
#
|
10
|
-
|
11
|
-
|
12
|
-
|
8
|
+
# Create a new Connection. This method takes a url (String) and options
|
9
|
+
# (Hash). These are passed to Excon, so any options valid for `Excon.new`
|
10
|
+
# can be passed here.
|
11
|
+
def initialize(url, opts)
|
12
|
+
case
|
13
|
+
when !url.is_a?(String)
|
14
|
+
raise ArgumentError, "Expected a String, got: '#{url}'"
|
15
|
+
when !opts.is_a?(Hash)
|
16
|
+
raise ArgumentError, "Expected a Hash, got: '#{opts}'"
|
17
|
+
else
|
18
|
+
@url, @options = url, opts
|
13
19
|
end
|
14
|
-
self.url = url
|
15
|
-
self.options = { :port => 4243 }.merge(options)
|
16
20
|
end
|
17
21
|
|
18
|
-
# The actual client that sends HTTP methods to the
|
22
|
+
# The actual client that sends HTTP methods to the Docker server. This value
|
23
|
+
# is not cached, since doing so may cause socket errors after bad requests.
|
19
24
|
def resource
|
20
|
-
|
25
|
+
Excon.new(url, options)
|
21
26
|
end
|
27
|
+
private :resource
|
22
28
|
|
23
|
-
#
|
24
|
-
|
25
|
-
|
26
|
-
|
29
|
+
# Send a request to the server with the `
|
30
|
+
def request(*args, &block)
|
31
|
+
resource.request(compile_request_params(*args, &block)).body
|
32
|
+
rescue Excon::Errors::BadRequest => ex
|
33
|
+
raise ClientError, ex.message
|
34
|
+
rescue Excon::Errors::InternalServerError => ex
|
35
|
+
raise ServerError, ex.message
|
27
36
|
end
|
28
37
|
|
29
|
-
# Delegate all HTTP methods to the
|
30
|
-
[:get, :put, :post, :delete
|
31
|
-
define_method(method)
|
32
|
-
begin
|
33
|
-
self.reset!
|
34
|
-
self.resource.public_send(method, *args, &block)
|
35
|
-
rescue Excon::Errors::BadRequest => ex
|
36
|
-
raise ClientError, ex.message
|
37
|
-
rescue Excon::Errors::InternalServerError => ex
|
38
|
-
raise ServerError, ex.message
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
# Send a request to the server and then parse it into a Hash.
|
44
|
-
def json_request(method, path, query = {}, &block)
|
45
|
-
params = compile_request_params(method, path, query, &block)
|
46
|
-
body = self.request(params).body
|
47
|
-
JSON.parse(body) unless body.nil? || body.empty? || (body == 'null')
|
48
|
-
rescue JSON::ParserError => ex
|
49
|
-
raise UnexpectedResponseError, ex.message
|
38
|
+
# Delegate all HTTP methods to the #request.
|
39
|
+
[:get, :put, :post, :delete].each do |method|
|
40
|
+
define_method(method) { |*args, &block| request(method, *args, &block) }
|
50
41
|
end
|
51
42
|
|
52
43
|
def to_s
|
53
|
-
"Docker::Connection { :url => #{
|
44
|
+
"Docker::Connection { :url => #{url}, :options => #{options} }"
|
54
45
|
end
|
55
46
|
|
56
47
|
private
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
48
|
+
# Given an HTTP method, path, optional query, extra options, and block,
|
49
|
+
# compiles a request.
|
50
|
+
def compile_request_params(http_method, path, query = nil, opts = nil, &block)
|
51
|
+
query ||= {}
|
52
|
+
opts ||= {}
|
53
|
+
headers = opts.delete(:headers) || {}
|
62
54
|
{
|
63
|
-
:method
|
64
|
-
:path
|
65
|
-
:query
|
66
|
-
:headers
|
67
|
-
|
68
|
-
|
69
|
-
:
|
70
|
-
:
|
71
|
-
|
55
|
+
:method => http_method,
|
56
|
+
:path => "/v#{Docker::API_VERSION}#{path}",
|
57
|
+
:query => query,
|
58
|
+
:headers => { 'Content-Type' => 'text/plain',
|
59
|
+
'User-Agent' => 'Docker-Client/0.4.6'
|
60
|
+
}.merge(headers),
|
61
|
+
:expects => (200..204),
|
62
|
+
:idempotent => http_method == :get,
|
63
|
+
:request_block => block
|
64
|
+
}.merge(opts).reject { |_, v| v.nil? }
|
72
65
|
end
|
73
66
|
end
|
data/lib/docker/container.rb
CHANGED
@@ -3,22 +3,14 @@
|
|
3
3
|
class Docker::Container
|
4
4
|
include Docker::Model
|
5
5
|
|
6
|
-
|
6
|
+
set_resource_prefix '/containers'
|
7
7
|
|
8
|
-
|
9
|
-
response =
|
10
|
-
|
11
|
-
:headers => { 'Content-Type' => 'text/plain',
|
12
|
-
'User-Agent' => "Docker-Client/0.4.6" },
|
13
|
-
:body => body.to_json,
|
14
|
-
:expects => (200..204)
|
15
|
-
)
|
16
|
-
@id = JSON.parse(response.body)['Id']
|
8
|
+
set_create_request do |body|
|
9
|
+
response = connection.post('/containers/create', nil, :body => body.to_json)
|
10
|
+
@id = Docker::Util.parse_json(response)['Id']
|
17
11
|
self
|
18
12
|
end
|
19
13
|
|
20
|
-
# Export the Container as a .tgz.
|
21
|
-
get :export
|
22
14
|
# Get more information about the Container.
|
23
15
|
get :json
|
24
16
|
# Wait for the current command to finish executing.
|
@@ -34,22 +26,22 @@ class Docker::Container
|
|
34
26
|
# Restart the Container
|
35
27
|
post :restart
|
36
28
|
|
29
|
+
# Export the Container as a tar.
|
30
|
+
def export(&block)
|
31
|
+
connection.get("/containers/#{id}/export", nil, :response_block => block)
|
32
|
+
true
|
33
|
+
end
|
34
|
+
|
37
35
|
# Attach to a container's standard streams / logs.
|
38
36
|
def attach(options = {})
|
39
37
|
options = { :stream => true, :stdout => true }.merge(options)
|
40
|
-
|
41
|
-
:path => "/containers/#{self.id}/attach",
|
42
|
-
:headers => { 'Content-Type' => 'text/plain',
|
43
|
-
'User-Agent' => "Docker-Client/0.4.6" },
|
44
|
-
:query => options,
|
45
|
-
:expects => (200..204)
|
46
|
-
).body
|
38
|
+
connection.post("/containers/#{id}/attach", options)
|
47
39
|
end
|
48
40
|
|
49
41
|
# Create an Image from a Container's change.s
|
50
42
|
def commit(options = {})
|
51
43
|
options.merge!('container' => self.id[0..7])
|
52
|
-
hash =
|
44
|
+
hash = JSON.parse(connection.request(:post, '/commit', options))
|
53
45
|
Docker::Image.send(:new, :id => hash['Id'], :connection => self.connection)
|
54
46
|
end
|
55
47
|
end
|
data/lib/docker/error.rb
CHANGED
data/lib/docker/image.rb
CHANGED
@@ -3,16 +3,11 @@ class Docker::Image
|
|
3
3
|
include Docker::Model
|
4
4
|
include Docker::Error
|
5
5
|
|
6
|
-
|
6
|
+
set_resource_prefix '/images'
|
7
7
|
|
8
|
-
|
9
|
-
body =
|
10
|
-
|
11
|
-
:headers => { 'User-Agent' => 'Docker-Client/0.4.6' },
|
12
|
-
:query => options,
|
13
|
-
:expects => (200..204)
|
14
|
-
).body
|
15
|
-
@id = JSON.parse(body)['status'] rescue nil
|
8
|
+
set_create_request do |options|
|
9
|
+
body = connection.post('/images/create', options)
|
10
|
+
@id = Docker::Util.parse_json(body)['status'] rescue nil
|
16
11
|
@id ||= options['fromImage']
|
17
12
|
@id ||= "#{options['repo']}/#{options['tag']}"
|
18
13
|
self
|
@@ -30,33 +25,19 @@ class Docker::Image
|
|
30
25
|
# to run the Image.
|
31
26
|
def run(cmd)
|
32
27
|
cmd = cmd.split(/\s+/) if cmd.is_a?(String)
|
33
|
-
Docker::Container.create({ 'Image' => self.id, 'Cmd' => cmd },
|
34
|
-
self.connection)
|
28
|
+
Docker::Container.create({ 'Image' => self.id, 'Cmd' => cmd }, connection)
|
35
29
|
.tap(&:start)
|
36
30
|
end
|
37
31
|
|
38
32
|
# Push the Image to the Docker registry.
|
39
33
|
def push(options = {})
|
40
|
-
|
41
|
-
:path => "/images/#{self.id}/push",
|
42
|
-
:headers => { 'Content-Type' => 'text/plain',
|
43
|
-
'User-Agent' => 'Docker-Client/0.4.6' },
|
44
|
-
:query => options,
|
45
|
-
:body => Docker.creds,
|
46
|
-
:expects => (200..204)
|
47
|
-
)
|
34
|
+
connection.post("/images/#{self.id}/push", options, :body => Docker.creds)
|
48
35
|
true
|
49
36
|
end
|
50
37
|
|
51
38
|
# Insert a file into the Image, returns a new Image that has that file.
|
52
39
|
def insert(query = {})
|
53
|
-
body =
|
54
|
-
:path => "/images/#{self.id}/insert",
|
55
|
-
:headers => { 'Content-Type' => 'text/plain',
|
56
|
-
'User-Agent' => "Docker-Client/0.4.6" },
|
57
|
-
:query => query,
|
58
|
-
:expects => (200..204)
|
59
|
-
).body
|
40
|
+
body = connection.post("/images/#{self.id}/insert", query)
|
60
41
|
if (id = body.match(/{"Id":"([a-f0-9]+)"}\z/)).nil? || id[1].empty?
|
61
42
|
raise UnexpectedResponseError, "Could not find Id in '#{body}'"
|
62
43
|
else
|
@@ -66,7 +47,7 @@ class Docker::Image
|
|
66
47
|
|
67
48
|
# Remove the Image from the server.
|
68
49
|
def remove
|
69
|
-
|
50
|
+
connection.request(:delete, "/images/#{self.id}", nil)
|
70
51
|
end
|
71
52
|
|
72
53
|
class << self
|
@@ -75,7 +56,8 @@ class Docker::Image
|
|
75
56
|
# Given a query like `{ :term => 'sshd' }`, queries the Docker Registry for
|
76
57
|
# a corresponiding Image.
|
77
58
|
def search(query = {}, connection = Docker.connection)
|
78
|
-
|
59
|
+
body = connection.request(:get, '/images/search', query)
|
60
|
+
hashes = Docker::Util.parse_json(body) || []
|
79
61
|
hashes.map { |hash| new(:id => hash['Name'], :connection => connection) }
|
80
62
|
end
|
81
63
|
|
@@ -83,24 +65,18 @@ class Docker::Image
|
|
83
65
|
def import(file, options = {}, connection = Docker.connection)
|
84
66
|
File.open(file, 'r') do |io|
|
85
67
|
body = connection.post(
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
).body
|
93
|
-
new(:id => JSON.parse(body)['status'], :connection => connection)
|
68
|
+
'/images/create',
|
69
|
+
options.merge('fromSrc' => '-'),
|
70
|
+
:headers => { 'Transfer-Encoding' => 'chunked' }
|
71
|
+
) { io.read(Excon.defaults[:chunk_size]).to_s }
|
72
|
+
new(:id => Docker::Util.parse_json(body)['status'],
|
73
|
+
:connection => connection)
|
94
74
|
end
|
95
75
|
end
|
96
76
|
|
97
77
|
# Given a Dockerfile as a string, builds an Image.
|
98
78
|
def build(commands, connection = Docker.connection)
|
99
|
-
body = connection.post(
|
100
|
-
:path => '/build',
|
101
|
-
:body => create_tar(commands),
|
102
|
-
:expects => (200..204)
|
103
|
-
).body
|
79
|
+
body = connection.post('/build', {}, :body => create_tar(commands))
|
104
80
|
new(:id => extract_id(body), :connection => connection)
|
105
81
|
end
|
106
82
|
|
@@ -108,12 +84,10 @@ class Docker::Image
|
|
108
84
|
def build_from_dir(dir, connection = Docker.connection)
|
109
85
|
tar = create_dir_tar(dir)
|
110
86
|
body = connection.post(
|
111
|
-
|
112
|
-
:headers
|
113
|
-
|
114
|
-
|
115
|
-
:expects => (200..204),
|
116
|
-
).body
|
87
|
+
'/build', {},
|
88
|
+
:headers => { 'Content-Type' => 'application/tar',
|
89
|
+
'Transfer-Encoding' => 'chunked' }
|
90
|
+
) { tar.read(Excon.defaults[:chunk_size]).to_s }
|
117
91
|
new(:id => extract_id(body), :connection => connection)
|
118
92
|
ensure
|
119
93
|
tar.close
|
data/lib/docker/model.rb
CHANGED
@@ -9,7 +9,7 @@ module Docker::Model
|
|
9
9
|
base.class_eval do
|
10
10
|
extend ClassMethods
|
11
11
|
private_class_method :new, :request, :get, :put, :post, :delete,
|
12
|
-
:
|
12
|
+
:set_create_request, :set_resource_prefix
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
@@ -17,12 +17,10 @@ module Docker::Model
|
|
17
17
|
# is specified and it is not a Docker::Connection, a
|
18
18
|
# Docker::Error::ArgumentError is raised.
|
19
19
|
def initialize(options = {})
|
20
|
-
options[:connection] ||= Docker.connection
|
21
|
-
|
22
|
-
raise ArgumentError, 'Expected a Docker::Connection.'
|
20
|
+
if (options[:connection] ||= Docker.connection).is_a?(Docker::Connection)
|
21
|
+
@id, @connection = options[:id], options[:connection]
|
23
22
|
else
|
24
|
-
|
25
|
-
@connection = options[:connection]
|
23
|
+
raise ArgumentError, 'Expected a Docker::Connection.'
|
26
24
|
end
|
27
25
|
end
|
28
26
|
|
@@ -33,24 +31,29 @@ module Docker::Model
|
|
33
31
|
# This defines the DSL for the including Classes.
|
34
32
|
module ClassMethods
|
35
33
|
include Docker::Error
|
34
|
+
attr_reader :resource_prefix, :create_request
|
36
35
|
|
37
36
|
# Define the Model's prefix for all requests.
|
38
|
-
def
|
39
|
-
|
37
|
+
def set_resource_prefix(val)
|
38
|
+
@resource_prefix = val
|
40
39
|
end
|
41
40
|
|
42
41
|
# Define how the Model should send a create request to the server.
|
43
|
-
def
|
44
|
-
|
42
|
+
def set_create_request(&block)
|
43
|
+
@create_request = block
|
45
44
|
end
|
46
45
|
|
47
46
|
# Define a method named `action` that sends an http `method` request to the
|
48
47
|
# Docker Server.
|
49
48
|
def request(method, action, opts = {}, &outer_block)
|
50
49
|
define_method(action) do |query = nil, &block|
|
51
|
-
|
52
|
-
|
53
|
-
|
50
|
+
new_opts = {
|
51
|
+
:path => "#{self.class.resource_prefix}/#{self.id}/#{action}",
|
52
|
+
:json => true
|
53
|
+
}.merge(opts)
|
54
|
+
body = connection.request(method, new_opts[:path], query,
|
55
|
+
new_opts[:excon], &block)
|
56
|
+
body = Docker::Util.parse_json(body) if new_opts[:json]
|
54
57
|
outer_block.nil? ? body : instance_exec(body, &outer_block)
|
55
58
|
end
|
56
59
|
end
|
@@ -71,7 +74,7 @@ module Docker::Model
|
|
71
74
|
# Retrieve every Instance of a model for the given server.
|
72
75
|
def all(options = {}, connection = Docker.connection)
|
73
76
|
path = "#{resource_prefix}/json"
|
74
|
-
hashes = connection.
|
77
|
+
hashes = Docker::Util.parse_json(connection.get(path, options)) || []
|
75
78
|
hashes.map { |hash| new(:id => hash['Id'], :connection => connection) }
|
76
79
|
end
|
77
80
|
end
|
data/lib/docker/util.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# This module holds shared logic that doesn't really belong anywhere else in the
|
2
|
+
# gem.
|
3
|
+
module Docker::Util
|
4
|
+
extend self
|
5
|
+
include Docker::Error
|
6
|
+
|
7
|
+
def parse_json(body)
|
8
|
+
JSON.parse(body) unless body.nil? || body.empty? || (body == 'null')
|
9
|
+
rescue JSON::ParserError => ex
|
10
|
+
raise UnexpectedResponseError, ex.message
|
11
|
+
end
|
12
|
+
end
|
data/lib/docker/version.rb
CHANGED
@@ -1,38 +1,34 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Docker::Connection do
|
4
|
+
subject { described_class.new('http://localhost', :port => 4243) }
|
5
|
+
|
4
6
|
describe '#initialize' do
|
5
|
-
|
7
|
+
let(:url) { 'http://localhost' }
|
8
|
+
let(:options) { { :port => 4243 } }
|
9
|
+
subject { described_class.new(url, options) }
|
6
10
|
|
7
|
-
context '
|
8
|
-
|
9
|
-
subject.new.options.should == { :port => 4243 }
|
10
|
-
end
|
11
|
+
context 'when the first argument is not a String' do
|
12
|
+
let(:url) { :lol_not_a_string }
|
11
13
|
|
12
|
-
it '
|
13
|
-
subject.
|
14
|
+
it 'raises an error' do
|
15
|
+
expect { subject }.to raise_error(Docker::Error::ArgumentError)
|
14
16
|
end
|
15
17
|
end
|
16
18
|
|
17
|
-
context '
|
18
|
-
context '
|
19
|
-
|
20
|
-
expect { subject.new('http://localhost', :lol) }
|
21
|
-
.to raise_error Docker::Error::ArgumentError
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
context 'when the argument is a Hash' do
|
26
|
-
let(:url) { 'google.com' }
|
27
|
-
let(:port) { 80 }
|
28
|
-
let(:options) { { :port => port } }
|
19
|
+
context 'when the first argument is a String' do
|
20
|
+
context 'but the second argument is not a Hash' do
|
21
|
+
let(:options) { :lol_not_a_hash }
|
29
22
|
|
30
|
-
it '
|
31
|
-
subject.
|
23
|
+
it 'raises an error' do
|
24
|
+
expect { subject }.to raise_error(Docker::Error::ArgumentError)
|
32
25
|
end
|
26
|
+
end
|
33
27
|
|
34
|
-
|
35
|
-
|
28
|
+
context 'and the second argument is a Hash' do
|
29
|
+
it 'sets the url and options' do
|
30
|
+
subject.url.should == url
|
31
|
+
subject.options.should == options
|
36
32
|
end
|
37
33
|
end
|
38
34
|
end
|
@@ -42,12 +38,42 @@ describe Docker::Connection do
|
|
42
38
|
its(:resource) { should be_a Excon::Connection }
|
43
39
|
end
|
44
40
|
|
41
|
+
describe '#request' do
|
42
|
+
let(:method) { :get }
|
43
|
+
let(:path) { '/test' }
|
44
|
+
let(:query) { { :all => true } }
|
45
|
+
let(:options) { { :expects => 201, :lol => true } }
|
46
|
+
let(:body) { rand(10000000) }
|
47
|
+
let(:resource) { mock(:resource) }
|
48
|
+
let(:response) { mock(:response, :body => body) }
|
49
|
+
let(:expected_hash) {
|
50
|
+
{
|
51
|
+
:method => method,
|
52
|
+
:path => "/v#{Docker::API_VERSION}#{path}",
|
53
|
+
:query => query,
|
54
|
+
:headers => { 'Content-Type' => 'text/plain',
|
55
|
+
'User-Agent' => 'Docker-Client/0.4.6' },
|
56
|
+
:expects => 201,
|
57
|
+
:idempotent => true,
|
58
|
+
:lol => true
|
59
|
+
}
|
60
|
+
}
|
61
|
+
|
62
|
+
before do
|
63
|
+
subject.stub(:resource).and_return(resource)
|
64
|
+
resource.should_receive(:request).with(expected_hash).and_return(response)
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'sends #request to #resource with the compiled params' do
|
68
|
+
subject.request(method, path, query, options).should == body
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
45
72
|
[:get, :put, :post, :delete].each do |method|
|
46
73
|
describe "##{method}" do
|
47
|
-
it 'is delegated to #
|
48
|
-
subject.should_receive(:
|
49
|
-
subject.
|
50
|
-
subject.public_send(method).should == :lol
|
74
|
+
it 'is delegated to #request' do
|
75
|
+
subject.should_receive(:request).with(method)
|
76
|
+
subject.public_send(method)
|
51
77
|
end
|
52
78
|
end
|
53
79
|
end
|
@@ -56,12 +82,12 @@ describe Docker::Connection do
|
|
56
82
|
let(:url) { 'google.com' }
|
57
83
|
let(:port) { 4000 }
|
58
84
|
let(:options) { { :port => port } }
|
59
|
-
let(:expected_string)
|
85
|
+
let(:expected_string) {
|
60
86
|
"Docker::Connection { :url => #{url}, :options => #{options} }"
|
61
|
-
|
87
|
+
}
|
62
88
|
subject { described_class.new(url, options) }
|
63
89
|
|
64
|
-
it 'returns a pretty
|
90
|
+
it 'returns a pretty version with the url and port' do
|
65
91
|
subject.to_s.should == expected_string
|
66
92
|
end
|
67
93
|
end
|