docker-api 1.4.0 → 1.5.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 +4 -4
- data/README.md +6 -6
- data/lib/docker.rb +4 -3
- data/lib/docker/connection.rb +2 -1
- data/lib/docker/container.rb +73 -38
- data/lib/docker/image.rb +63 -27
- data/lib/docker/util.rb +2 -1
- data/lib/docker/version.rb +1 -1
- data/spec/docker/container_spec.rb +14 -14
- data/spec/docker/image_spec.rb +1 -8
- data/spec/vcr/Docker_Container/_create/when_the_Container_does_not_yet_exist/when_the_HTTP_request_returns_a_200/sets_the_id.yml +30 -0
- data/spec/vcr/Docker_Container/_start/starts_the_container.yml +103 -35
- metadata +4 -3
- data/lib/docker/model.rb +0 -77
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 649da0fe1cfd65917ce9975d649ba38846ce728c
|
4
|
+
data.tar.gz: 17a7e5506eccab710826b563b8fa74961f3d31ff
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0884b950410fc3d3d8b169dbc6561efd26e08640cf9d5a0439e36ef743eb2decdb880378639b090da4f9e0a1380cf9fe5aef5829a9eb7b169112c33df893b6e6
|
7
|
+
data.tar.gz: 26c0b55692549c0f77f6a946967c228f5f86c005fdb555b2366dd1e015fcccab157b77fae1c31dc7a3ed2ab2346cba9295299b29fb617632ca16fa036f146015
|
data/README.md
CHANGED
@@ -2,7 +2,7 @@ docker-api
|
|
2
2
|
==========
|
3
3
|
[](http://badge.fury.io/rb/docker-api) [](https://travis-ci.org/swipely/docker-api) [](https://codeclimate.com/github/swipely/docker-api) [](https://gemnasium.com/swipely/docker-api)
|
4
4
|
|
5
|
-
This gem provides an object-oriented interface to the [Docker Remote API](http://docs.docker.io/en/latest/api/docker_remote_api_v1.4/). Every method listed there is implemented, with the exception of attaching to the STDIN of a Container. At the time of this writing, docker-api is meant to interface with Docker version 0.5
|
5
|
+
This gem provides an object-oriented interface to the [Docker Remote API](http://docs.docker.io/en/latest/api/docker_remote_api_v1.4/). Every method listed there is implemented, with the exception of attaching to the STDIN of a Container. At the time of this writing, docker-api is meant to interface with Docker version 0.5.*.
|
6
6
|
|
7
7
|
Installation
|
8
8
|
------------
|
@@ -74,7 +74,7 @@ require 'docker'
|
|
74
74
|
# => true
|
75
75
|
|
76
76
|
Docker.version
|
77
|
-
# => { 'Version' => '0.
|
77
|
+
# => { 'Version' => '0.5.2', 'GoVersion' => 'go1.1' }
|
78
78
|
|
79
79
|
Docker.info
|
80
80
|
# => { "Debug" => false, "Containers" => 187, "Images" => 196, "NFd" => 10, "NGoroutines" => 9, "MemoryLimit" => true }
|
@@ -160,19 +160,19 @@ container.json
|
|
160
160
|
|
161
161
|
# Start running the Container.
|
162
162
|
container.start
|
163
|
-
# =>
|
163
|
+
# => Docker::Container { :id => 492510dd38e4, :connection => Docker::Connection { :url => http://localhost, :options => {:port=>4243} } }
|
164
164
|
|
165
165
|
# Stop running the Container.
|
166
166
|
container.stop
|
167
|
-
# =>
|
167
|
+
# => Docker::Container { :id => 492510dd38e4, :connection => Docker::Connection { :url => http://localhost, :options => {:port=>4243} } }
|
168
168
|
|
169
169
|
# Restart the Container.
|
170
170
|
container.restart
|
171
|
-
# =>
|
171
|
+
# => Docker::Container { :id => 492510dd38e4, :connection => Docker::Connection { :url => http://localhost, :options => {:port=>4243} } }
|
172
172
|
|
173
173
|
# Kill the command running in the Container.
|
174
174
|
container.kill
|
175
|
-
# =>
|
175
|
+
# => Docker::Container { :id => 492510dd38e4, :connection => Docker::Connection { :url => http://localhost, :options => {:port=>4243} } }
|
176
176
|
|
177
177
|
# Return the currently executing processes in a Container.
|
178
178
|
container.top
|
data/lib/docker.rb
CHANGED
@@ -8,8 +8,6 @@ require 'archive/tar/minitar'
|
|
8
8
|
# The top-level module for this gem. It's purpose is to hold global
|
9
9
|
# configuration variables that are used as defaults in other classes.
|
10
10
|
module Docker
|
11
|
-
extend self
|
12
|
-
|
13
11
|
attr_reader :creds
|
14
12
|
|
15
13
|
def url
|
@@ -64,12 +62,15 @@ module Docker
|
|
64
62
|
rescue Docker::Error::DockerError
|
65
63
|
raise Docker::Error::VersionError, "Expected API Version: #{API_VERSION}"
|
66
64
|
end
|
65
|
+
|
66
|
+
module_function :url, :url=, :options, :options=, :connection,
|
67
|
+
:reset_connection!, :version, :info, :authenticate!,
|
68
|
+
:validate_version!
|
67
69
|
end
|
68
70
|
|
69
71
|
require 'docker/version'
|
70
72
|
require 'docker/error'
|
71
73
|
require 'docker/util'
|
72
74
|
require 'docker/connection'
|
73
|
-
require 'docker/model'
|
74
75
|
require 'docker/container'
|
75
76
|
require 'docker/image'
|
data/lib/docker/connection.rb
CHANGED
@@ -53,12 +53,13 @@ private
|
|
53
53
|
query ||= {}
|
54
54
|
opts ||= {}
|
55
55
|
headers = opts.delete(:headers) || {}
|
56
|
+
content_type = opts[:body].nil? ? 'text/plain' : 'application/json'
|
56
57
|
user_agent = "Swipely/Docker-API #{Docker::VERSION}"
|
57
58
|
{
|
58
59
|
:method => http_method,
|
59
60
|
:path => "/v#{Docker::API_VERSION}#{path}",
|
60
61
|
:query => query,
|
61
|
-
:headers => { 'Content-Type' =>
|
62
|
+
:headers => { 'Content-Type' => content_type,
|
62
63
|
'User-Agent' => user_agent,
|
63
64
|
}.merge(headers),
|
64
65
|
:expects => (200..204),
|
data/lib/docker/container.rb
CHANGED
@@ -1,48 +1,32 @@
|
|
1
1
|
# This class represents a Docker Container. It's important to note that nothing
|
2
2
|
# is cached so that the information is always up to date.
|
3
3
|
class Docker::Container
|
4
|
-
include Docker::Model
|
5
4
|
include Docker::Error
|
6
5
|
|
7
|
-
|
6
|
+
attr_accessor :id, :connection
|
8
7
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
8
|
+
# The private new method accepts a connection and optional id.
|
9
|
+
def initialize(connection, id = nil)
|
10
|
+
if connection.is_a?(Docker::Connection)
|
11
|
+
@connection, @id = connection, id
|
12
|
+
else
|
13
|
+
raise ArgumentError, "Expected a Docker::Connection, got: #{connection}."
|
14
|
+
end
|
13
15
|
end
|
14
16
|
|
15
|
-
# Get more information about the Container.
|
16
|
-
request :get, :json
|
17
|
-
# Start the Container.
|
18
|
-
request :post, :start
|
19
|
-
# Inspect the Container's changes to the filesysetem
|
20
|
-
request :get, :changes
|
21
|
-
# Stop the Container.
|
22
|
-
request :post, :stop
|
23
|
-
# Kill the Container.
|
24
|
-
request :post, :kill
|
25
|
-
# Restart the Container
|
26
|
-
request :post, :restart
|
27
|
-
|
28
17
|
# Return a List of Hashes that represents the top running processes.
|
29
18
|
def top(opts = {})
|
30
|
-
resp = connection.get(
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
# For each method, `m`, define a method called `m?` that attempts the method,
|
36
|
-
# but catches all Server errors.
|
37
|
-
[:stop, :start, :kill, :restart].each do |method|
|
38
|
-
define_method :"#{method}?" do |*args|
|
39
|
-
begin; public_send(method, *args); rescue ServerError; end
|
19
|
+
resp = Docker::Util.parse_json(connection.get(path_for(:top), opts))
|
20
|
+
if resp['Processes'].nil?
|
21
|
+
[]
|
22
|
+
else
|
23
|
+
resp['Processes'].map { |ary| Hash[resp['Titles'].zip(ary)] }
|
40
24
|
end
|
41
25
|
end
|
42
26
|
|
43
27
|
# Wait for the current command to finish executing.
|
44
28
|
def wait(time = 60)
|
45
|
-
resp = connection.post(
|
29
|
+
resp = connection.post(path_for(:wait), nil, :read_timeout => time)
|
46
30
|
Docker::Util.parse_json(resp)
|
47
31
|
end
|
48
32
|
|
@@ -51,8 +35,8 @@ class Docker::Container
|
|
51
35
|
# the command that is currently executing does not return a 0 status code, an
|
52
36
|
# UnexpectedResponseError is raised.
|
53
37
|
def run(cmd, time = 1000)
|
54
|
-
if (code = tap(&:start
|
55
|
-
commit.run(cmd).tap(&:start
|
38
|
+
if (code = tap(&:start).wait(time)['StatusCode']).zero?
|
39
|
+
commit.run(cmd).tap(&:start)
|
56
40
|
else
|
57
41
|
raise UnexpectedResponseError, "Command returned status code #{code}."
|
58
42
|
end
|
@@ -60,21 +44,72 @@ class Docker::Container
|
|
60
44
|
|
61
45
|
# Export the Container as a tar.
|
62
46
|
def export(&block)
|
63
|
-
connection.get(
|
64
|
-
|
47
|
+
connection.get(path_for(:export), {}, :response_block => block)
|
48
|
+
self
|
65
49
|
end
|
66
50
|
|
67
51
|
# Attach to a container's standard streams / logs.
|
68
52
|
def attach(options = {}, &block)
|
69
|
-
|
70
|
-
connection.post(
|
71
|
-
:response_block => block)
|
53
|
+
opts = { :stream => true, :stdout => true }.merge(options)
|
54
|
+
connection.post(path_for(:attach), opts, :response_block => block)
|
72
55
|
end
|
73
56
|
|
74
57
|
# Create an Image from a Container's change.s
|
75
58
|
def commit(options = {})
|
76
59
|
options.merge!('container' => self.id[0..7])
|
77
60
|
hash = Docker::Util.parse_json(connection.post('/commit', options))
|
78
|
-
Docker::Image.send(:new,
|
61
|
+
Docker::Image.send(:new, self.connection, hash['Id'])
|
62
|
+
end
|
63
|
+
|
64
|
+
# Return a String represntation of the Container.
|
65
|
+
def to_s
|
66
|
+
"Docker::Container { :id => #{self.id}, :connection => #{self.connection} }"
|
79
67
|
end
|
68
|
+
|
69
|
+
# #json returns information about the Container, #changes returns a list of
|
70
|
+
# the changes the Container has made to the filesystem.
|
71
|
+
[:json, :changes].each do |method|
|
72
|
+
define_method(method) do |opts = {}|
|
73
|
+
Docker::Util.parse_json(connection.get(path_for(method), opts))
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# #start!, #stop!, #kill!, and #restart! all perform the associated action and
|
78
|
+
# return the Container. #start, #stop, #kill, and #restart all do the same,
|
79
|
+
# but rescue from ServerErrors.
|
80
|
+
[:start, :stop, :kill, :restart].each do |method|
|
81
|
+
define_method(:"#{method}!") do |opts = {}|
|
82
|
+
connection.post(path_for(method), {}, :body => opts.to_json)
|
83
|
+
self
|
84
|
+
end
|
85
|
+
|
86
|
+
define_method(method) do |*args|
|
87
|
+
begin; public_send(:"#{method}!", *args); rescue ServerError; self end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Create a new Container.
|
92
|
+
def self.create(opts = {}, conn = Docker.connection)
|
93
|
+
instance = new(conn)
|
94
|
+
resp = conn.post('/containers/create', {}, :body => opts.to_json)
|
95
|
+
if (instance.id = Docker::Util.parse_json(resp)['Id']).nil?
|
96
|
+
raise UnexpectedResponseError, 'Create response did not contain an Id'
|
97
|
+
else
|
98
|
+
instance
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Return all of the Containers.
|
103
|
+
def self.all(opts = {}, conn = Docker.connection)
|
104
|
+
hashes = Docker::Util.parse_json(conn.get('/containers/json', opts)) || []
|
105
|
+
hashes.map { |hash| new(conn, hash['Id']) }
|
106
|
+
end
|
107
|
+
|
108
|
+
# Convenience method to return the path for a particular resource.
|
109
|
+
def path_for(resource)
|
110
|
+
"/containers/#{self.id}/#{resource}"
|
111
|
+
end
|
112
|
+
|
113
|
+
private :path_for
|
114
|
+
private_class_method :new
|
80
115
|
end
|
data/lib/docker/image.rb
CHANGED
@@ -1,47 +1,45 @@
|
|
1
1
|
# This class represents a Docker Image.
|
2
2
|
class Docker::Image
|
3
|
-
include Docker::Model
|
4
3
|
include Docker::Error
|
5
4
|
|
6
|
-
|
5
|
+
attr_accessor :id, :connection
|
7
6
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
7
|
+
# The private new method accepts a connection and optional id.
|
8
|
+
def initialize(connection, id = nil)
|
9
|
+
if connection.is_a?(Docker::Connection)
|
10
|
+
@connection, @id = connection, id
|
11
|
+
else
|
12
|
+
raise ArgumentError, "Expected a Docker::Connection, got: #{connection}."
|
13
|
+
end
|
14
14
|
end
|
15
15
|
|
16
|
-
# Tag the Image.
|
17
|
-
request :post, :tag
|
18
|
-
# Get more information about the Image.
|
19
|
-
request :get, :json
|
20
|
-
# Get the history of the Image.
|
21
|
-
request :get, :history
|
22
|
-
|
23
16
|
# Given a command and optional list of streams to attach to, run a command on
|
24
17
|
# an Image. This will not modify the Image, but rather create a new Container
|
25
18
|
# to run the Image.
|
26
19
|
def run(cmd)
|
27
20
|
cmd = cmd.split(/\s+/) if cmd.is_a?(String)
|
28
21
|
Docker::Container.create({ 'Image' => self.id, 'Cmd' => cmd }, connection)
|
29
|
-
.tap(&:start)
|
22
|
+
.tap(&:start!)
|
30
23
|
end
|
31
24
|
|
32
25
|
# Push the Image to the Docker registry.
|
33
26
|
def push(options = {})
|
34
|
-
connection.post(
|
35
|
-
|
27
|
+
connection.post(path_for(:push), options, :body => Docker.creds)
|
28
|
+
self
|
29
|
+
end
|
30
|
+
|
31
|
+
# Tag the Image.
|
32
|
+
def tag(opts = {})
|
33
|
+
Docker::Util.parse_json(connection.post(path_for(:tag), opts))
|
36
34
|
end
|
37
35
|
|
38
36
|
# Insert a file into the Image, returns a new Image that has that file.
|
39
37
|
def insert(query = {})
|
40
|
-
body = connection.post(
|
38
|
+
body = connection.post(path_for(:insert), query)
|
41
39
|
if (id = body.match(/{"Id":"([a-f0-9]+)"}\z/)).nil? || id[1].empty?
|
42
40
|
raise UnexpectedResponseError, "Could not find Id in '#{body}'"
|
43
41
|
else
|
44
|
-
self.class.send(:new,
|
42
|
+
self.class.send(:new, connection, id[1])
|
45
43
|
end
|
46
44
|
end
|
47
45
|
|
@@ -50,15 +48,46 @@ class Docker::Image
|
|
50
48
|
connection.delete("/images/#{self.id}")
|
51
49
|
end
|
52
50
|
|
51
|
+
# Return a String representation of the Image.
|
52
|
+
def to_s
|
53
|
+
"Docker::Image { :id => #{self.id}, :connection => #{self.connection} }"
|
54
|
+
end
|
55
|
+
|
56
|
+
# #json returns extra information about an Image, #history returns its
|
57
|
+
# history.
|
58
|
+
[:json, :history].each do |method|
|
59
|
+
define_method(method) do |opts = {}|
|
60
|
+
Docker::Util.parse_json(connection.get(path_for(method), opts))
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
53
64
|
class << self
|
54
65
|
include Docker::Error
|
55
66
|
|
67
|
+
# Create a new Image.
|
68
|
+
def create(opts = {}, conn = Docker.connection)
|
69
|
+
instance = new(conn)
|
70
|
+
conn.post('/images/create', opts)
|
71
|
+
id = opts['repo'] ? "#{opts['repo']}/#{opts['tag']}" : opts['fromImage']
|
72
|
+
if (instance.id = id).nil?
|
73
|
+
raise UnexpectedResponseError, 'Create response did not contain an Id'
|
74
|
+
else
|
75
|
+
instance
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Return every Image.
|
80
|
+
def all(opts = {}, conn = Docker.connection)
|
81
|
+
hashes = Docker::Util.parse_json(conn.get('/images/json', opts)) || []
|
82
|
+
hashes.map { |hash| new(conn, hash['Id']) }
|
83
|
+
end
|
84
|
+
|
56
85
|
# Given a query like `{ :term => 'sshd' }`, queries the Docker Registry for
|
57
|
-
# a
|
86
|
+
# a corresponding Image.
|
58
87
|
def search(query = {}, connection = Docker.connection)
|
59
88
|
body = connection.get('/images/search', query)
|
60
89
|
hashes = Docker::Util.parse_json(body) || []
|
61
|
-
hashes.map { |hash| new(
|
90
|
+
hashes.map { |hash| new(connection, hash['Name']) }
|
62
91
|
end
|
63
92
|
|
64
93
|
# Import an Image from the output of Docker::Container#export.
|
@@ -67,17 +96,17 @@ class Docker::Image
|
|
67
96
|
body = connection.post(
|
68
97
|
'/images/create',
|
69
98
|
options.merge('fromSrc' => '-'),
|
70
|
-
:headers => { '
|
99
|
+
:headers => { 'Content-Type' => 'application/tar',
|
100
|
+
'Transfer-Encoding' => 'chunked' }
|
71
101
|
) { io.read(Excon.defaults[:chunk_size]).to_s }
|
72
|
-
new(
|
73
|
-
:connection => connection)
|
102
|
+
new(connection, Docker::Util.parse_json(body)['status'])
|
74
103
|
end
|
75
104
|
end
|
76
105
|
|
77
106
|
# Given a Dockerfile as a string, builds an Image.
|
78
107
|
def build(commands, connection = Docker.connection)
|
79
108
|
body = connection.post('/build', {}, :body => create_tar(commands))
|
80
|
-
new(
|
109
|
+
new(connection, extract_id(body))
|
81
110
|
end
|
82
111
|
|
83
112
|
# Given a directory that contains a Dockerfile, builds an Image.
|
@@ -88,7 +117,7 @@ class Docker::Image
|
|
88
117
|
:headers => { 'Content-Type' => 'application/tar',
|
89
118
|
'Transfer-Encoding' => 'chunked' }
|
90
119
|
) { tar.read(Excon.defaults[:chunk_size]).to_s }
|
91
|
-
new(
|
120
|
+
new(connection, extract_id(body))
|
92
121
|
ensure
|
93
122
|
tar.close unless tar.nil?
|
94
123
|
end
|
@@ -121,4 +150,11 @@ class Docker::Image
|
|
121
150
|
FileUtils.cd(cwd)
|
122
151
|
end
|
123
152
|
end
|
153
|
+
|
154
|
+
# Convenience method to return the path for a particular resource.
|
155
|
+
def path_for(resource)
|
156
|
+
"/images/#{self.id}/#{resource}"
|
157
|
+
end
|
158
|
+
|
159
|
+
private :path_for
|
124
160
|
end
|
data/lib/docker/util.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# This module holds shared logic that doesn't really belong anywhere else in the
|
2
2
|
# gem.
|
3
3
|
module Docker::Util
|
4
|
-
extend self
|
5
4
|
include Docker::Error
|
6
5
|
|
7
6
|
def parse_json(body)
|
@@ -9,4 +8,6 @@ module Docker::Util
|
|
9
8
|
rescue JSON::ParserError => ex
|
10
9
|
raise UnexpectedResponseError, ex.message
|
11
10
|
end
|
11
|
+
|
12
|
+
module_function :parse_json
|
12
13
|
end
|
data/lib/docker/version.rb
CHANGED
@@ -4,7 +4,7 @@ require 'spec_helper'
|
|
4
4
|
# Docker daemon and have the base Image pulled.
|
5
5
|
describe Docker::Container do
|
6
6
|
describe '#to_s' do
|
7
|
-
subject { described_class.send(:new,
|
7
|
+
subject { described_class.send(:new, Docker.connection, rand(10000).to_s) }
|
8
8
|
|
9
9
|
let(:id) { 'bf119e2' }
|
10
10
|
let(:connection) { Docker.connection }
|
@@ -91,13 +91,20 @@ describe Docker::Container do
|
|
91
91
|
end
|
92
92
|
|
93
93
|
describe '#start' do
|
94
|
-
subject {
|
94
|
+
subject {
|
95
|
+
described_class.create(
|
96
|
+
'Cmd' => %w[test -d /foo],
|
97
|
+
'Image' => 'base',
|
98
|
+
'Volumes' => {'/foo' => {}}
|
99
|
+
)
|
100
|
+
}
|
101
|
+
let(:all) { Docker::Container.all }
|
102
|
+
|
103
|
+
before { subject.start('Binds' => ["/tmp:/foo"]) }
|
95
104
|
|
96
105
|
it 'starts the container', :vcr do
|
97
|
-
subject.
|
98
|
-
|
99
|
-
id.start_with?(subject.id)
|
100
|
-
}
|
106
|
+
all.map(&:id).should be_any { |id| id.start_with?(subject.id) }
|
107
|
+
subject.wait(10)['StatusCode'].should be_zero
|
101
108
|
end
|
102
109
|
end
|
103
110
|
|
@@ -214,14 +221,7 @@ describe Docker::Container do
|
|
214
221
|
describe '.create' do
|
215
222
|
subject { described_class }
|
216
223
|
|
217
|
-
context 'when the
|
218
|
-
it 'raises an error' do
|
219
|
-
expect { subject.create(:not_a_hash) }
|
220
|
-
.to raise_error(Docker::Error::ArgumentError)
|
221
|
-
end
|
222
|
-
end
|
223
|
-
|
224
|
-
context 'when the Container does not yet exist and the body is a Hash' do
|
224
|
+
context 'when the Container does not yet exist' do
|
225
225
|
context 'when the HTTP request does not return a 200' do
|
226
226
|
before { Excon.stub({ :method => :post }, { :status => 400 }) }
|
227
227
|
after { Excon.stubs.shift }
|
data/spec/docker/image_spec.rb
CHANGED
@@ -2,7 +2,7 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe Docker::Image do
|
4
4
|
describe '#to_s' do
|
5
|
-
subject { described_class.send(:new,
|
5
|
+
subject { described_class.send(:new, Docker.connection, id) }
|
6
6
|
|
7
7
|
let(:id) { 'bf119e2' }
|
8
8
|
let(:connection) { Docker.connection }
|
@@ -102,13 +102,6 @@ describe Docker::Image do
|
|
102
102
|
describe '.create' do
|
103
103
|
subject { described_class }
|
104
104
|
|
105
|
-
context 'when the body is not a Hash' do
|
106
|
-
it 'raises an error' do
|
107
|
-
expect { subject.create(:not_a_hash) }
|
108
|
-
.to raise_error(Docker::Error::ArgumentError)
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
105
|
context 'when the Image does not yet exist and the body is a Hash' do
|
113
106
|
let(:image) { subject.create('fromImage' => 'base') }
|
114
107
|
|
@@ -0,0 +1,30 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: post
|
5
|
+
uri: http://localhost:4243/v1.4/containers/create
|
6
|
+
body:
|
7
|
+
encoding: UTF-8
|
8
|
+
string: '{"Hostname":"","User":"","Memory":0,"MemorySwap":0,"AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"PortSpecs":null,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":null,"Cmd":["date"],"Dns":null,"Image":"base","Volumes":{},"VolumesFrom":""}'
|
9
|
+
headers:
|
10
|
+
Content-Type:
|
11
|
+
- text/plain
|
12
|
+
User-Agent:
|
13
|
+
- Swipely/Docker-API 1.4.0
|
14
|
+
response:
|
15
|
+
status:
|
16
|
+
code: 201
|
17
|
+
message: ''
|
18
|
+
headers:
|
19
|
+
Content-Type:
|
20
|
+
- text/plain; charset=utf-8
|
21
|
+
Content-Length:
|
22
|
+
- '21'
|
23
|
+
Date:
|
24
|
+
- Thu, 08 Aug 2013 14:47:05 GMT
|
25
|
+
body:
|
26
|
+
encoding: UTF-8
|
27
|
+
string: '{"Id":"3e4587069eb0"}'
|
28
|
+
http_version:
|
29
|
+
recorded_at: Thu, 08 Aug 2013 14:47:05 GMT
|
30
|
+
recorded_with: VCR 2.4.0
|
@@ -5,55 +5,61 @@ http_interactions:
|
|
5
5
|
uri: http://localhost:4243/v1.4/containers/create
|
6
6
|
body:
|
7
7
|
encoding: UTF-8
|
8
|
-
string: '{"Cmd":["
|
8
|
+
string: ! '{"Cmd":["test","-d","/foo"],"Image":"base","Volumes":{"/foo":{}}}'
|
9
9
|
headers:
|
10
10
|
Content-Type:
|
11
11
|
- text/plain
|
12
12
|
User-Agent:
|
13
|
-
- Swipely/Docker-API 1.
|
13
|
+
- Swipely/Docker-API 1.4.0
|
14
14
|
response:
|
15
15
|
status:
|
16
16
|
code: 201
|
17
17
|
message: ''
|
18
18
|
headers:
|
19
|
-
|
20
|
-
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
19
|
+
!binary "Q29udGVudC1UeXBl":
|
20
|
+
- !binary |-
|
21
|
+
dGV4dC9wbGFpbjsgY2hhcnNldD11dGYtOA==
|
22
|
+
!binary "Q29udGVudC1MZW5ndGg=":
|
23
|
+
- !binary |-
|
24
|
+
MjE=
|
25
|
+
!binary "RGF0ZQ==":
|
26
|
+
- !binary |-
|
27
|
+
RnJpLCAwOSBBdWcgMjAxMyAwODo1MDowNSBHTVQ=
|
25
28
|
body:
|
26
|
-
encoding:
|
27
|
-
string: '{"Id":"
|
29
|
+
encoding: US-ASCII
|
30
|
+
string: ! '{"Id":"3632688f5774"}'
|
28
31
|
http_version:
|
29
|
-
recorded_at:
|
32
|
+
recorded_at: Fri, 09 Aug 2013 08:50:06 GMT
|
30
33
|
- request:
|
31
34
|
method: post
|
32
|
-
uri: http://localhost:4243/v1.4/containers/
|
35
|
+
uri: http://localhost:4243/v1.4/containers/3632688f5774/start
|
33
36
|
body:
|
34
|
-
encoding:
|
35
|
-
string: ''
|
37
|
+
encoding: UTF-8
|
38
|
+
string: ! '{"Binds":["/tmp:/foo"]}'
|
36
39
|
headers:
|
37
40
|
Content-Type:
|
38
|
-
-
|
41
|
+
- application/json
|
39
42
|
User-Agent:
|
40
|
-
- Swipely/Docker-API 1.
|
43
|
+
- Swipely/Docker-API 1.4.0
|
41
44
|
response:
|
42
45
|
status:
|
43
46
|
code: 204
|
44
47
|
message: ''
|
45
48
|
headers:
|
46
|
-
|
47
|
-
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
49
|
+
!binary "Q29udGVudC1UeXBl":
|
50
|
+
- !binary |-
|
51
|
+
dGV4dC9wbGFpbjsgY2hhcnNldD11dGYtOA==
|
52
|
+
!binary "Q29udGVudC1MZW5ndGg=":
|
53
|
+
- !binary |-
|
54
|
+
MA==
|
55
|
+
!binary "RGF0ZQ==":
|
56
|
+
- !binary |-
|
57
|
+
RnJpLCAwOSBBdWcgMjAxMyAwODo1MDowNiBHTVQ=
|
52
58
|
body:
|
53
|
-
encoding:
|
59
|
+
encoding: US-ASCII
|
54
60
|
string: ''
|
55
61
|
http_version:
|
56
|
-
recorded_at:
|
62
|
+
recorded_at: Fri, 09 Aug 2013 08:50:06 GMT
|
57
63
|
- request:
|
58
64
|
method: get
|
59
65
|
uri: http://localhost:4243/v1.4/containers/json
|
@@ -64,22 +70,84 @@ http_interactions:
|
|
64
70
|
Content-Type:
|
65
71
|
- text/plain
|
66
72
|
User-Agent:
|
67
|
-
- Swipely/Docker-API 1.
|
73
|
+
- Swipely/Docker-API 1.4.0
|
68
74
|
response:
|
69
75
|
status:
|
70
76
|
code: 200
|
71
77
|
message: ''
|
78
|
+
headers:
|
79
|
+
!binary "Q29udGVudC1UeXBl":
|
80
|
+
- !binary |-
|
81
|
+
YXBwbGljYXRpb24vanNvbg==
|
82
|
+
!binary "Q29udGVudC1MZW5ndGg=":
|
83
|
+
- !binary |-
|
84
|
+
OTQ2
|
85
|
+
!binary "RGF0ZQ==":
|
86
|
+
- !binary |-
|
87
|
+
RnJpLCAwOSBBdWcgMjAxMyAwODo1MDowNiBHTVQ=
|
88
|
+
body:
|
89
|
+
encoding: US-ASCII
|
90
|
+
string: ! '[{"Id":"3632688f5774a1a5077e0b29e4410458ae43ec9dbaa400b10e68b385206e0cff","Image":"base:latest","Command":"test
|
91
|
+
-d /foo","Created":1376038205,"Status":"Up Less than a second","Ports":"","SizeRw":0,"SizeRootFs":0},{"Id":"965d1be64f580f590fa712f6e34b1186cd937f1448b6d5feeeb02174dd562cba","Image":"c401009f3872","Command":"/bin/bash
|
92
|
+
--login -c cd /app && bundle exec rackup -p $PORT","Created":1376001906,"Status":"Up
|
93
|
+
10 hours","Ports":"","SizeRw":0,"SizeRootFs":0},{"Id":"371a31ee0515e231b53331827e2169a781d605259ca75f0d1b1593095fee6924","Image":"69e523818a5f","Command":"/bin/bash
|
94
|
+
--login -c cd /app && bundle exec rackup config.ru -p 80","Created":1375967943,"Status":"Up
|
95
|
+
19 hours","Ports":"","SizeRw":0,"SizeRootFs":0},{"Id":"c7a4fe69a7249d6db5f24106891e1c28ee4bfe538ece09a53574c0b91850f165","Image":"samalba/hipache:latest","Command":"supervisord
|
96
|
+
-n","Created":1375657256,"Status":"Up 19 hours","Ports":"80-\u003e80","SizeRw":0,"SizeRootFs":0}]'
|
97
|
+
http_version:
|
98
|
+
recorded_at: Fri, 09 Aug 2013 08:50:06 GMT
|
99
|
+
- request:
|
100
|
+
method: post
|
101
|
+
uri: http://localhost:4243/v1.4/containers/3632688f5774/wait
|
102
|
+
body:
|
103
|
+
encoding: US-ASCII
|
104
|
+
string: ''
|
72
105
|
headers:
|
73
106
|
Content-Type:
|
74
|
-
-
|
75
|
-
|
76
|
-
-
|
77
|
-
|
78
|
-
|
107
|
+
- text/plain
|
108
|
+
User-Agent:
|
109
|
+
- Swipely/Docker-API 1.4.0
|
110
|
+
response:
|
111
|
+
status:
|
112
|
+
code: 200
|
113
|
+
message: ''
|
114
|
+
headers:
|
115
|
+
!binary "Q29udGVudC1UeXBl":
|
116
|
+
- !binary |-
|
117
|
+
YXBwbGljYXRpb24vanNvbg==
|
118
|
+
!binary "Q29udGVudC1MZW5ndGg=":
|
119
|
+
- !binary |-
|
120
|
+
MTY=
|
121
|
+
!binary "RGF0ZQ==":
|
122
|
+
- !binary |-
|
123
|
+
RnJpLCAwOSBBdWcgMjAxMyAwODo1MDowNiBHTVQ=
|
79
124
|
body:
|
80
|
-
encoding:
|
81
|
-
string: '
|
82
|
-
|
125
|
+
encoding: US-ASCII
|
126
|
+
string: ! '{"StatusCode":0}'
|
127
|
+
http_version:
|
128
|
+
recorded_at: Fri, 09 Aug 2013 08:50:06 GMT
|
129
|
+
- request:
|
130
|
+
method: post
|
131
|
+
uri: http://localhost:4243/v1.4/containers/3632688f5774/attach?logs=true&stderr=true&stdout=true&stream=false
|
132
|
+
body:
|
133
|
+
encoding: US-ASCII
|
134
|
+
string: ''
|
135
|
+
headers:
|
136
|
+
Content-Type:
|
137
|
+
- text/plain
|
138
|
+
User-Agent:
|
139
|
+
- Swipely/Docker-API 1.4.0
|
140
|
+
response:
|
141
|
+
status:
|
142
|
+
code: 200
|
143
|
+
message: ''
|
144
|
+
headers:
|
145
|
+
!binary "Q29udGVudC1UeXBl":
|
146
|
+
- !binary |-
|
147
|
+
YXBwbGljYXRpb24vdm5kLmRvY2tlci5yYXctc3RyZWFt
|
148
|
+
body:
|
149
|
+
encoding: US-ASCII
|
150
|
+
string: ''
|
83
151
|
http_version:
|
84
|
-
recorded_at:
|
85
|
-
recorded_with: VCR 2.
|
152
|
+
recorded_at: Fri, 09 Aug 2013 08:50:06 GMT
|
153
|
+
recorded_with: VCR 2.5.0
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: docker-api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Swipely, Inc.
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-08-
|
11
|
+
date: 2013-08-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: excon
|
@@ -171,7 +171,6 @@ files:
|
|
171
171
|
- lib/docker/container.rb
|
172
172
|
- lib/docker/error.rb
|
173
173
|
- lib/docker/image.rb
|
174
|
-
- lib/docker/model.rb
|
175
174
|
- lib/docker/util.rb
|
176
175
|
- lib/docker/version.rb
|
177
176
|
- spec/docker/connection_spec.rb
|
@@ -189,6 +188,7 @@ files:
|
|
189
188
|
- spec/vcr/Docker_Container/_attach/yields_each_chunk.yml
|
190
189
|
- spec/vcr/Docker_Container/_changes/returns_the_changes_as_an_array.yml
|
191
190
|
- spec/vcr/Docker_Container/_commit/creates_a_new_Image_from_the_Container_s_changes.yml
|
191
|
+
- spec/vcr/Docker_Container/_create/when_the_Container_does_not_yet_exist/when_the_HTTP_request_returns_a_200/sets_the_id.yml
|
192
192
|
- 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
|
193
193
|
- spec/vcr/Docker_Container/_export/yields_each_chunk.yml
|
194
194
|
- spec/vcr/Docker_Container/_json/returns_the_description_as_a_Hash.yml
|
@@ -254,6 +254,7 @@ test_files:
|
|
254
254
|
- spec/vcr/Docker_Container/_attach/yields_each_chunk.yml
|
255
255
|
- spec/vcr/Docker_Container/_changes/returns_the_changes_as_an_array.yml
|
256
256
|
- spec/vcr/Docker_Container/_commit/creates_a_new_Image_from_the_Container_s_changes.yml
|
257
|
+
- spec/vcr/Docker_Container/_create/when_the_Container_does_not_yet_exist/when_the_HTTP_request_returns_a_200/sets_the_id.yml
|
257
258
|
- 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
|
258
259
|
- spec/vcr/Docker_Container/_export/yields_each_chunk.yml
|
259
260
|
- spec/vcr/Docker_Container/_json/returns_the_description_as_a_Hash.yml
|
data/lib/docker/model.rb
DELETED
@@ -1,77 +0,0 @@
|
|
1
|
-
# This module is intended to be used as a Mixin for all objects exposed by the
|
2
|
-
# Remote API. Currently, these are limited to Containers and Images.
|
3
|
-
module Docker::Model
|
4
|
-
include Docker::Error
|
5
|
-
|
6
|
-
attr_reader :id, :connection
|
7
|
-
|
8
|
-
def self.included(base)
|
9
|
-
base.class_eval do
|
10
|
-
extend ClassMethods
|
11
|
-
private_class_method :new, :request, :set_create_request,
|
12
|
-
:set_resource_prefix
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
# Creates a new Model with the specified id and Connection. If a Connection
|
17
|
-
# is specified and it is not a Docker::Connection, a
|
18
|
-
# Docker::Error::ArgumentError is raised.
|
19
|
-
def initialize(options = {})
|
20
|
-
if (options[:connection] ||= Docker.connection).is_a?(Docker::Connection)
|
21
|
-
@id, @connection = options[:id], options[:connection]
|
22
|
-
else
|
23
|
-
raise ArgumentError, 'Expected a Docker::Connection.'
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def to_s
|
28
|
-
"#{self.class.name} { :id => #{id}, :connection => #{connection} }"
|
29
|
-
end
|
30
|
-
|
31
|
-
# This defines the DSL for the including Classes.
|
32
|
-
module ClassMethods
|
33
|
-
include Docker::Error
|
34
|
-
attr_reader :resource_prefix, :create_request
|
35
|
-
|
36
|
-
# Define the Model's prefix for all requests.
|
37
|
-
def set_resource_prefix(val)
|
38
|
-
@resource_prefix = val
|
39
|
-
end
|
40
|
-
|
41
|
-
# Define how the Model should send a create request to the server.
|
42
|
-
def set_create_request(&block)
|
43
|
-
@create_request = block
|
44
|
-
end
|
45
|
-
|
46
|
-
# Define a method named `action` that sends an http `method` request to the
|
47
|
-
# Docker Server.
|
48
|
-
def request(method, action, opts = {}, &outer_block)
|
49
|
-
define_method(action) do |query = nil, &block|
|
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]
|
57
|
-
outer_block.nil? ? body : instance_exec(body, &outer_block)
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
# Create a Model with the specified body. Raises a
|
62
|
-
# Docker::Error::ArgumentError if the argument is not a Hash. Otherwise,
|
63
|
-
# instances execs the Class's #create_request method with the single
|
64
|
-
# argument.
|
65
|
-
def create(opts = {}, conn = Docker.connection)
|
66
|
-
raise Docker::Error::ArgumentError, 'Expected a Hash' if !opts.is_a?(Hash)
|
67
|
-
new(:connection => conn).instance_exec(opts, &create_request)
|
68
|
-
end
|
69
|
-
|
70
|
-
# Retrieve every Instance of a model for the given server.
|
71
|
-
def all(options = {}, connection = Docker.connection)
|
72
|
-
path = "#{resource_prefix}/json"
|
73
|
-
hashes = Docker::Util.parse_json(connection.get(path, options)) || []
|
74
|
-
hashes.map { |hash| new(:id => hash['Id'], :connection => connection) }
|
75
|
-
end
|
76
|
-
end
|
77
|
-
end
|