docker-api 1.4.0 → 1.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Gem Version](https://badge.fury.io/rb/docker-api.png)](http://badge.fury.io/rb/docker-api) [![travis-ci](https://travis-ci.org/swipely/docker-api.png?branch=master)](https://travis-ci.org/swipely/docker-api) [![Code Climate](https://codeclimate.com/github/swipely/docker-api.png)](https://codeclimate.com/github/swipely/docker-api) [![Dependency Status](https://gemnasium.com/swipely/docker-api.png)](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
|