docker-api 1.6.0 → 1.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.cane +0 -1
- data/README.md +6 -7
- data/docker-api.gemspec +3 -4
- data/lib/docker.rb +10 -5
- data/lib/docker/connection.rb +2 -0
- data/lib/docker/container.rb +39 -4
- data/lib/docker/error.rb +6 -0
- data/lib/docker/event.rb +40 -0
- data/lib/docker/image.rb +37 -13
- data/lib/docker/util.rb +25 -1
- data/lib/docker/version.rb +2 -2
- data/spec/docker/container_spec.rb +37 -19
- data/spec/docker/event_spec.rb +79 -0
- data/spec/docker/image_spec.rb +89 -30
- data/spec/docker/util_spec.rb +83 -0
- data/spec/docker_spec.rb +47 -2
- data/spec/fixtures/{Dockerfile → build_from_dir/Dockerfile} +0 -0
- data/spec/fixtures/top/Dockerfile +2 -0
- data/spec/support/vcr.rb +1 -4
- data/spec/vcr/Docker/_authenticate_/with_valid_credentials/logs_in_and_sets_the_creds.yml +33 -0
- data/spec/vcr/Docker/_info/returns_the_info_as_a_Hash.yml +7 -7
- data/spec/vcr/Docker/_validate_version/when_nothing_is_raised/validate_version_/.yml +6 -6
- data/spec/vcr/Docker/_version/returns_the_version_as_a_Hash.yml +6 -6
- data/spec/vcr/Docker_Container/_all/when_the_HTTP_response_is_a_200/materializes_each_Container_into_a_Docker_Container.yml +27 -97
- data/spec/vcr/Docker_Container/_attach/yields_each_chunk.yml +18 -19
- data/spec/vcr/Docker_Container/_changes/returns_the_changes_as_an_array.yml +22 -22
- data/spec/vcr/Docker_Container/_commit/creates_a_new_Image_from_the_Container_s_changes.yml +18 -18
- data/spec/vcr/Docker_Container/_commit/if_run_is_passed_it_saves_the_command_in_the_image/saves_the_command.yml +178 -0
- data/spec/vcr/Docker_Container/_copy/when_the_file_does_not_exist/raises_an_error.yml +54 -24
- data/spec/vcr/Docker_Container/_copy/when_the_input_is_a_directory/yields_each_chunk_of_the_tarred_directory.yml +27 -60
- data/spec/vcr/Docker_Container/_copy/when_the_input_is_a_file/yields_each_chunk_of_the_tarred_file.yml +27 -60
- data/spec/vcr/Docker_Container/_create/when_the_Container_does_not_yet_exist/when_the_HTTP_request_returns_a_200/sets_the_id.yml +7 -7
- data/spec/vcr/Docker_Container/_delete/deletes_the_container.yml +16 -46
- data/spec/vcr/Docker_Container/_export/yields_each_chunk.yml +24 -17
- data/spec/vcr/Docker_Container/_json/returns_the_description_as_a_Hash.yml +14 -14
- data/spec/vcr/Docker_Container/_kill/kills_the_container.yml +36 -49
- data/spec/vcr/Docker_Container/_restart/restarts_the_container.yml +43 -43
- data/spec/vcr/Docker_Container/_run/when_the_Container_s_command_does_not_return_status_code_of_0/raises_an_error.yml +17 -17
- data/spec/vcr/Docker_Container/_run/when_the_Container_s_command_returns_a_status_code_of_0/creates_a_new_container_to_run_the_specified_command.yml +47 -47
- data/spec/vcr/Docker_Container/_start/starts_the_container.yml +25 -25
- data/spec/vcr/Docker_Container/_stop/stops_the_container.yml +40 -53
- data/spec/vcr/Docker_Container/_top/returns_the_top_commands_as_an_Array.yml +89 -25
- data/spec/vcr/Docker_Container/_wait/waits_for_the_command_to_finish.yml +17 -17
- data/spec/vcr/Docker_Container/_wait/when_an_argument_is_given/and_a_command_runs_for_too_long/raises_a_ServerError.yml +12 -12
- data/spec/vcr/Docker_Container/_wait/when_an_argument_is_given/sets_the_read_timeout_to_that_amount_of_time.yml +17 -17
- data/spec/vcr/Docker_Image/_all/materializes_each_Image_into_a_Docker_Image.yml +12 -12
- data/spec/vcr/Docker_Image/_build/with_a_valid_Dockerfile/with_specifying_a_repo_in_the_query_parameters/builds_an_image_and_tags_it.yml +110 -0
- data/spec/vcr/Docker_Image/_build/with_a_valid_Dockerfile/{builds_an_image.yml → without_query_parameters/builds_an_image.yml} +5 -5
- data/spec/vcr/Docker_Image/_build/with_an_invalid_Dockerfile/throws_a_UnexpectedResponseError.yml +7 -9
- data/spec/vcr/Docker_Image/_build_from_dir/with_a_valid_Dockerfile/with_no_query_parameters/builds_the_image.yml +121 -0
- data/spec/vcr/Docker_Image/_build_from_dir/with_a_valid_Dockerfile/with_specifying_a_repo_in_the_query_parameters/builds_the_image_and_tags_it.yml +151 -0
- data/spec/vcr/Docker_Image/_create/when_the_Image_does_not_yet_exist_and_the_body_is_a_Hash/sets_the_id.yml +5 -5
- data/spec/vcr/Docker_Image/_history/returns_the_history_of_the_Image.yml +10 -10
- data/spec/vcr/Docker_Image/_insert/inserts_the_url_s_file_into_a_new_Image.yml +53 -109
- data/spec/vcr/Docker_Image/_insert_local/when_the_local_file_does_exist/creates_a_new_Image_that_has_that_file.yml +32 -68
- data/spec/vcr/Docker_Image/_insert_local/when_the_local_file_does_not_exist/raises_an_error.yml +5 -5
- data/spec/vcr/Docker_Image/_insert_local/when_there_are_multiple_files_passed/creates_a_new_Image_that_has_each_file.yml +40 -68
- data/spec/vcr/Docker_Image/_json/returns_additional_information_about_image_image.yml +10 -10
- data/spec/vcr/Docker_Image/_push/pushes_the_Image.yml +161 -0
- data/spec/vcr/Docker_Image/_remove/removes_the_Image.yml +17 -17
- data/spec/vcr/Docker_Image/_run/when_the_argument_is_a_String/splits_the_String_by_spaces_and_creates_a_new_Container.yml +23 -24
- data/spec/vcr/Docker_Image/_run/when_the_argument_is_an_Array/creates_a_new_Container.yml +23 -24
- data/spec/vcr/Docker_Image/_run/when_the_argument_is_nil/command_configured_in_image/should_normally_show_result_if_image_has_Cmd_configured.yml +148 -0
- data/spec/vcr/Docker_Image/_run/when_the_argument_is_nil/no_command_configured_in_image/should_raise_an_error_if_no_command_is_specified.yml +68 -0
- data/spec/vcr/Docker_Image/_search/materializes_each_Image_into_a_Docker_Image.yml +13 -11
- data/spec/vcr/Docker_Image/_tag/tags_the_image_with_the_repo_name.yml +10 -10
- metadata +32 -24
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a4a13f5948a8df770804ade2a22dc3c31e7fc1b1
|
4
|
+
data.tar.gz: c3ac3bb3d1219fb34fbb1e91b4ee63e7fc4486b9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ff874b82925ea1dd68a05be8bdad589108967abc86d6a649efaf07606b35266284c4d596e0bdf603f56d7a5d8268a6f327a9e90311b0b7240cd9b517b909ac1f
|
7
|
+
data.tar.gz: abd742b0ee713c369d1034b90c96ed6e8c6ba48ccac6fa36129ecd36c5a8c6340860cf89486d90feaacebf105a30f6df8ec17e5954dd63c85cc73dcd0cf19953
|
data/.cane
CHANGED
@@ -1 +0,0 @@
|
|
1
|
-
--abc-max 13
|
data/README.md
CHANGED
@@ -219,22 +219,21 @@ ff02::1 ip6-allnodes
|
|
219
219
|
ff02::2 ip6-allrouters
|
220
220
|
# => Docker::Container { :id => a1759f3e2873, :connection => Docker::Connection { :url => http://localhost, :options => {:port=>4243} } }
|
221
221
|
|
222
|
-
|
223
|
-
|
224
222
|
# Wait for the current command to finish executing. If an argument is given,
|
225
223
|
# will timeout after that number of seconds. The default is one minute.
|
226
224
|
container.wait(15)
|
227
225
|
# => {'StatusCode'=>0}
|
228
226
|
|
229
227
|
# Attach to the Container. Currently, the below options are the only valid ones.
|
230
|
-
# By default, :stream and :
|
228
|
+
# By default, :stream, :stdout, and :stderr are set.
|
231
229
|
container.attach(:stream => true, :stdout => true, :stderr => true, :logs => true)
|
232
|
-
# => "bin\nboot\ndev\netc\nhome\nlib\nlib64\nmedia\nmnt\nopt\nproc\nroot\nrun\nsbin\nselinux\nsrv\nsys\ntmp\nusr\nvar"
|
230
|
+
# => [["bin\nboot\ndev\netc\nhome\nlib\nlib64\nmedia\nmnt\nopt\nproc\nroot\nrun\nsbin\nselinux\nsrv\nsys\ntmp\nusr\nvar", []]
|
233
231
|
|
234
232
|
# If you wish to stream the attach method, a block may be supplied.
|
235
|
-
container = Docker::Container.create('Image' => 'base', 'Cmd' =>
|
236
|
-
container.tap(&:start).attach { |chunk| puts chunk }
|
237
|
-
|
233
|
+
container = Docker::Container.create('Image' => 'base', 'Cmd' => ['find / -name *'])
|
234
|
+
container.tap(&:start).attach { |stream, chunk| puts "#{stream}: #{chunk}" }
|
235
|
+
stderr: 2013/10/30 17:16:24 Unable to locate find / -name *
|
236
|
+
# => [[], ["2013/10/30 17:16:24 Unable to locate find / -name *\n"]]
|
238
237
|
|
239
238
|
# Create an Image from a Container's changes.
|
240
239
|
container.commit
|
data/docker-api.gemspec
CHANGED
@@ -3,7 +3,7 @@ require File.expand_path('../lib/docker/version', __FILE__)
|
|
3
3
|
|
4
4
|
Gem::Specification.new do |gem|
|
5
5
|
gem.authors = ["Swipely, Inc."]
|
6
|
-
gem.email = %w{tomhulihan@swipely.com bright@swipely.com}
|
6
|
+
gem.email = %w{tomhulihan@swipely.com bright@swipely.com toddlunter@swipely.com}
|
7
7
|
gem.description = %q{A simple REST client for the Docker Remote API}
|
8
8
|
gem.summary = %q{A simple REST client for the Docker Remote API}
|
9
9
|
gem.homepage = 'https://github.com/swipely/docker-api'
|
@@ -14,14 +14,13 @@ Gem::Specification.new do |gem|
|
|
14
14
|
gem.name = "docker-api"
|
15
15
|
gem.require_paths = %w{lib}
|
16
16
|
gem.version = Docker::VERSION
|
17
|
-
gem.add_dependency 'excon', '>= 0.
|
17
|
+
gem.add_dependency 'excon', '>= 0.28'
|
18
18
|
gem.add_dependency 'json'
|
19
19
|
gem.add_dependency 'archive-tar-minitar'
|
20
20
|
gem.add_development_dependency 'rake'
|
21
21
|
gem.add_development_dependency 'rspec'
|
22
22
|
gem.add_development_dependency 'cane'
|
23
23
|
gem.add_development_dependency 'pry'
|
24
|
-
gem.add_development_dependency '
|
25
|
-
gem.add_development_dependency 'vcr', '>= 2.4.0'
|
24
|
+
gem.add_development_dependency 'vcr', '>= 2.7.0'
|
26
25
|
gem.add_development_dependency 'simplecov'
|
27
26
|
end
|
data/lib/docker.rb
CHANGED
@@ -2,13 +2,14 @@ require 'cgi'
|
|
2
2
|
require 'json'
|
3
3
|
require 'excon'
|
4
4
|
require 'tempfile'
|
5
|
+
require 'base64'
|
5
6
|
require 'rubygems/package'
|
6
7
|
require 'archive/tar/minitar'
|
7
8
|
|
8
9
|
# The top-level module for this gem. It's purpose is to hold global
|
9
10
|
# configuration variables that are used as defaults in other classes.
|
10
11
|
module Docker
|
11
|
-
|
12
|
+
attr_accessor :creds
|
12
13
|
|
13
14
|
def default_socket_url
|
14
15
|
'unix:///var/run/docker.sock'
|
@@ -56,9 +57,12 @@ module Docker
|
|
56
57
|
|
57
58
|
# Login to the Docker registry.
|
58
59
|
def authenticate!(options = {})
|
59
|
-
|
60
|
-
connection.post(
|
60
|
+
creds = options.to_json
|
61
|
+
connection.post('/auth', {}, :body => creds)
|
62
|
+
@creds = creds
|
61
63
|
true
|
64
|
+
rescue Docker::Error::ServerError, Docker::Error::UnauthorizedError
|
65
|
+
raise Docker::Error::AuthenticationError
|
62
66
|
end
|
63
67
|
|
64
68
|
# When the correct version of Docker is installed, returns true. Otherwise,
|
@@ -71,8 +75,8 @@ module Docker
|
|
71
75
|
end
|
72
76
|
|
73
77
|
module_function :default_socket_url, :env_url, :url, :url=, :options,
|
74
|
-
:options=, :connection, :reset_connection!,
|
75
|
-
:info, :authenticate!, :validate_version!
|
78
|
+
:options=, :creds, :creds=, :connection, :reset_connection!,
|
79
|
+
:version, :info, :authenticate!, :validate_version!
|
76
80
|
end
|
77
81
|
|
78
82
|
require 'docker/version'
|
@@ -81,3 +85,4 @@ require 'docker/util'
|
|
81
85
|
require 'docker/connection'
|
82
86
|
require 'docker/container'
|
83
87
|
require 'docker/image'
|
88
|
+
require 'docker/event'
|
data/lib/docker/connection.rb
CHANGED
@@ -36,6 +36,8 @@ class Docker::Connection
|
|
36
36
|
resource.request(compile_request_params(*args, &block)).body
|
37
37
|
rescue Excon::Errors::BadRequest => ex
|
38
38
|
raise ClientError, ex.message
|
39
|
+
rescue Excon::Errors::Unauthorized => ex
|
40
|
+
raise UnauthorizedError, ex.message
|
39
41
|
rescue Excon::Errors::InternalServerError => ex
|
40
42
|
raise ServerError, ex.message
|
41
43
|
rescue Excon::Errors::Timeout => ex
|
data/lib/docker/container.rb
CHANGED
@@ -50,14 +50,31 @@ class Docker::Container
|
|
50
50
|
|
51
51
|
# Attach to a container's standard streams / logs.
|
52
52
|
def attach(options = {}, &block)
|
53
|
-
opts = {
|
54
|
-
|
53
|
+
opts = {
|
54
|
+
:stream => true, :stdout => true, :stderr => true
|
55
|
+
}.merge(options)
|
56
|
+
# Creates list to store stdout and stderr messages
|
57
|
+
msgs = [[],[]]
|
58
|
+
connection.post(
|
59
|
+
path_for(:attach),
|
60
|
+
opts,
|
61
|
+
:response_block => attach_for(block, msgs)
|
62
|
+
)
|
63
|
+
msgs
|
55
64
|
end
|
56
65
|
|
57
66
|
# Create an Image from a Container's change.s
|
58
67
|
def commit(options = {})
|
59
68
|
options.merge!('container' => self.id[0..7])
|
60
|
-
|
69
|
+
# [code](https://github.com/dotcloud/docker/blob/v0.6.3/commands.go#L1115)
|
70
|
+
# Based on the link, the config passed as run, needs to be passed as the
|
71
|
+
# body of the post so capture it, remove from the options, and pass it via
|
72
|
+
# the post body
|
73
|
+
config = options.delete('run')
|
74
|
+
hash = Docker::Util.parse_json(connection.post('/commit',
|
75
|
+
options,
|
76
|
+
:body => config.to_json)
|
77
|
+
)
|
61
78
|
Docker::Image.send(:new, self.connection, hash['Id'])
|
62
79
|
end
|
63
80
|
|
@@ -124,6 +141,24 @@ class Docker::Container
|
|
124
141
|
"/containers/#{self.id}/#{resource}"
|
125
142
|
end
|
126
143
|
|
127
|
-
|
144
|
+
# Method that takes chunks and calls the attached block for each mux'd message
|
145
|
+
def attach_for(block, msg_stack)
|
146
|
+
lambda do |c,r,t|
|
147
|
+
stdout_msgs, stderr_msgs = Docker::Util.decipher_messages(c)
|
148
|
+
msg_stack[0] += stdout_msgs
|
149
|
+
msg_stack[1] += stderr_msgs
|
150
|
+
|
151
|
+
unless block.nil?
|
152
|
+
stdout_msgs.each do |msg|
|
153
|
+
block.call(:stdout, msg)
|
154
|
+
end
|
155
|
+
stderr_msgs.each do |msg|
|
156
|
+
block.call(:stderr, msg)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
private :path_for, :attach_for
|
128
163
|
private_class_method :new
|
129
164
|
end
|
data/lib/docker/error.rb
CHANGED
@@ -11,6 +11,9 @@ module Docker::Error
|
|
11
11
|
# Raised when a request returns a 400.
|
12
12
|
class ClientError < DockerError; end
|
13
13
|
|
14
|
+
# Raised when a request returns a 401.
|
15
|
+
class UnauthorizedError < DockerError; end
|
16
|
+
|
14
17
|
# Raised when a request returns a 500.
|
15
18
|
class ServerError < DockerError; end
|
16
19
|
|
@@ -22,4 +25,7 @@ module Docker::Error
|
|
22
25
|
|
23
26
|
# Raised when a request times out.
|
24
27
|
class TimeoutError < DockerError; end
|
28
|
+
|
29
|
+
# Raised when login fails.
|
30
|
+
class AuthenticationError < DockerError; end
|
25
31
|
end
|
data/lib/docker/event.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# This class represents a Docker Image.
|
2
|
+
class Docker::Event
|
3
|
+
include Docker::Error
|
4
|
+
|
5
|
+
attr_accessor :status, :id, :from, :time
|
6
|
+
|
7
|
+
def initialize(status, id, from, time)
|
8
|
+
@status, @id, @from, @time = status, id, from, time
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_s
|
12
|
+
"Docker::Event { :status => #{self.status}, :id => #{self.id}, "\
|
13
|
+
":from => #{self.from}, :time => #{self.time} }"
|
14
|
+
end
|
15
|
+
|
16
|
+
class << self
|
17
|
+
include Docker::Error
|
18
|
+
|
19
|
+
def stream(opts = {}, conn = Docker.connection, &block)
|
20
|
+
conn.get('/events', opts, :response_block => lambda { |b, r, t|
|
21
|
+
block.call(new_event(b, r, t))
|
22
|
+
})
|
23
|
+
end
|
24
|
+
|
25
|
+
def since(since, opts = {}, conn = Docker.connection, &block)
|
26
|
+
stream(opts.merge(:since => since), conn, &block)
|
27
|
+
end
|
28
|
+
|
29
|
+
def new_event(body, remaining, total)
|
30
|
+
return if body.nil? || body.empty?
|
31
|
+
json = Docker::Util.parse_json(body)
|
32
|
+
Docker::Event.new(
|
33
|
+
json['status'],
|
34
|
+
json['id'],
|
35
|
+
json['from'],
|
36
|
+
json['time']
|
37
|
+
)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/docker/image.rb
CHANGED
@@ -15,16 +15,32 @@ class Docker::Image
|
|
15
15
|
|
16
16
|
# Given a command and optional list of streams to attach to, run a command on
|
17
17
|
# an Image. This will not modify the Image, but rather create a new Container
|
18
|
-
# to run the Image.
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
18
|
+
# to run the Image. If the image has an embedded config, no command is
|
19
|
+
# necessary, but it will fail with 500 if no config is saved with the image
|
20
|
+
def run(cmd=nil)
|
21
|
+
opts = { 'Image' => self.id }
|
22
|
+
opts["Cmd"] = cmd.split(/\s+/) if cmd.is_a?(String)
|
23
|
+
begin
|
24
|
+
Docker::Container.create(opts, connection)
|
25
|
+
.tap(&:start!)
|
26
|
+
rescue ServerError
|
27
|
+
if cmd
|
28
|
+
raise ServerError, "Docker Server Error."
|
29
|
+
else
|
30
|
+
raise ServerError, "No command specified."
|
31
|
+
end
|
32
|
+
end
|
23
33
|
end
|
24
34
|
|
25
35
|
# Push the Image to the Docker registry.
|
26
|
-
def push(options = {})
|
27
|
-
|
36
|
+
def push(creds = nil, options = {})
|
37
|
+
credentials = (creds.nil?) ? Docker.creds : creds.to_json
|
38
|
+
headers = Docker::Util.build_auth_header(credentials)
|
39
|
+
connection.post(
|
40
|
+
path_for(:push),
|
41
|
+
options,
|
42
|
+
:headers => headers
|
43
|
+
)
|
28
44
|
self
|
29
45
|
end
|
30
46
|
|
@@ -83,8 +99,14 @@ class Docker::Image
|
|
83
99
|
include Docker::Error
|
84
100
|
|
85
101
|
# Create a new Image.
|
86
|
-
def create(opts = {}, conn = Docker.connection)
|
87
|
-
|
102
|
+
def create(opts = {}, creds = nil, conn = Docker.connection)
|
103
|
+
credentials = (creds.nil?) ? creds.to_json : Docker.creds
|
104
|
+
headers = if credentials.nil?
|
105
|
+
Docker::Util.build_auth_header(credentials)
|
106
|
+
else
|
107
|
+
{}
|
108
|
+
end
|
109
|
+
instance = new(conn, {}, :headers => headers)
|
88
110
|
conn.post('/images/create', opts)
|
89
111
|
id = opts['repo'] ? "#{opts['repo']}/#{opts['tag']}" : opts['fromImage']
|
90
112
|
if (instance.id = id).nil?
|
@@ -122,19 +144,21 @@ class Docker::Image
|
|
122
144
|
end
|
123
145
|
|
124
146
|
# Given a Dockerfile as a string, builds an Image.
|
125
|
-
def build(commands, connection = Docker.connection)
|
147
|
+
def build(commands, opts = {}, connection = Docker.connection)
|
126
148
|
body = connection.post(
|
127
|
-
'/build',
|
149
|
+
'/build', opts,
|
128
150
|
:body => Docker::Util.create_tar('Dockerfile' => commands)
|
129
151
|
)
|
130
152
|
new(connection, Docker::Util.extract_id(body))
|
153
|
+
rescue Docker::Error::ServerError
|
154
|
+
raise Docker::Error::UnexpectedResponseError
|
131
155
|
end
|
132
156
|
|
133
157
|
# Given a directory that contains a Dockerfile, builds an Image.
|
134
|
-
def build_from_dir(dir, connection = Docker.connection)
|
158
|
+
def build_from_dir(dir, opts = {}, connection = Docker.connection)
|
135
159
|
tar = Docker::Util.create_dir_tar(dir)
|
136
160
|
body = connection.post(
|
137
|
-
'/build',
|
161
|
+
'/build', opts,
|
138
162
|
:headers => { 'Content-Type' => 'application/tar',
|
139
163
|
'Transfer-Encoding' => 'chunked' }
|
140
164
|
) { tar.read(Excon.defaults[:chunk_size]).to_s }
|
data/lib/docker/util.rb
CHANGED
@@ -18,7 +18,7 @@ module Docker::Util
|
|
18
18
|
tar.add_file(file_name, 0640) { |tar_file| tar_file.write(input) }
|
19
19
|
end
|
20
20
|
end
|
21
|
-
output.tap(&:rewind)
|
21
|
+
output.tap(&:rewind).string
|
22
22
|
end
|
23
23
|
|
24
24
|
def create_dir_tar(directory)
|
@@ -57,4 +57,28 @@ module Docker::Util
|
|
57
57
|
|
58
58
|
file_hash
|
59
59
|
end
|
60
|
+
|
61
|
+
def build_auth_header(credentials)
|
62
|
+
credentials = credentials.to_json if credentials.is_a?(Hash)
|
63
|
+
{ 'X-Registry-Auth' => Base64.encode64(credentials).gsub(/\n/, '') }
|
64
|
+
end
|
65
|
+
|
66
|
+
# Method to break apart application/vnd.docker.raw-stream headers
|
67
|
+
def decipher_messages(body)
|
68
|
+
raw_text = body.dup
|
69
|
+
stdout_messages = []
|
70
|
+
stderr_messages = []
|
71
|
+
while !raw_text.empty?
|
72
|
+
header = raw_text.slice!(0,8)
|
73
|
+
next if header.nil?
|
74
|
+
length = header[4..7].chars
|
75
|
+
.map { |c| c.getbyte(0) }
|
76
|
+
.inject(0) { |total, curr| (total << 8) + curr }
|
77
|
+
message = raw_text.slice!(0,length)
|
78
|
+
stdout_messages << message if header.getbyte(0) == 1
|
79
|
+
stderr_messages << message if header.getbyte(0) == 2
|
80
|
+
end
|
81
|
+
|
82
|
+
[stdout_messages, stderr_messages]
|
83
|
+
end
|
60
84
|
end
|
data/lib/docker/version.rb
CHANGED
@@ -58,16 +58,14 @@ describe Docker::Container do
|
|
58
58
|
end
|
59
59
|
|
60
60
|
describe '#top' do
|
61
|
-
|
62
|
-
|
63
|
-
'Cmd' => %w[while true; do; done;],
|
64
|
-
'Image' => 'base'
|
65
|
-
)
|
61
|
+
let(:dir) {
|
62
|
+
File.join(File.dirname(__FILE__), '..', 'fixtures', 'top')
|
66
63
|
}
|
67
|
-
let(:
|
64
|
+
let(:image) { Docker::Image.build_from_dir(dir) }
|
65
|
+
let(:top) { sleep 1; container.top }
|
66
|
+
let!(:container) { image.run('/while') }
|
68
67
|
|
69
|
-
|
70
|
-
after { subject.kill }
|
68
|
+
after { container.kill; image.remove }
|
71
69
|
|
72
70
|
it 'returns the top commands as an Array', :vcr do
|
73
71
|
top.should be_a Array
|
@@ -80,7 +78,7 @@ describe Docker::Container do
|
|
80
78
|
subject {
|
81
79
|
Docker::Image.create(
|
82
80
|
'fromImage' => 'base'
|
83
|
-
).run('touch /test').tap { |c| c.
|
81
|
+
).run('touch /test').tap { |c| c.wait }
|
84
82
|
}
|
85
83
|
|
86
84
|
context 'when the file does not exist' do
|
@@ -120,8 +118,7 @@ describe Docker::Container do
|
|
120
118
|
it 'yields each chunk', :vcr do
|
121
119
|
first = nil
|
122
120
|
subject.export do |chunk|
|
123
|
-
first
|
124
|
-
break
|
121
|
+
first ||= chunk
|
125
122
|
end
|
126
123
|
first[257..261].should == "ustar" # Make sure the export is a tar.
|
127
124
|
end
|
@@ -133,10 +130,11 @@ describe Docker::Container do
|
|
133
130
|
before { subject.start }
|
134
131
|
|
135
132
|
it 'yields each chunk', :vcr do
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
133
|
+
chunk = nil
|
134
|
+
subject.attach do |stream, c|
|
135
|
+
chunk ||= c
|
136
|
+
end
|
137
|
+
expect(chunk).to eq("/\n")
|
140
138
|
end
|
141
139
|
end
|
142
140
|
|
@@ -277,6 +275,14 @@ describe Docker::Container do
|
|
277
275
|
image.should be_a Docker::Image
|
278
276
|
image.id.should_not be_nil
|
279
277
|
end
|
278
|
+
|
279
|
+
context 'if run is passed, it saves the command in the image', :vcr do
|
280
|
+
let(:image) { subject.commit('run' => {"Cmd" => %w[pwd]}) }
|
281
|
+
it 'saves the command' do
|
282
|
+
expect(image.run.attach).to eql [["/\n"],[]]
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
280
286
|
end
|
281
287
|
|
282
288
|
describe '.create' do
|
@@ -284,8 +290,14 @@ describe Docker::Container do
|
|
284
290
|
|
285
291
|
context 'when the Container does not yet exist' do
|
286
292
|
context 'when the HTTP request does not return a 200' do
|
287
|
-
before
|
288
|
-
|
293
|
+
before do
|
294
|
+
Docker.options = { :mock => true }
|
295
|
+
Excon.stub({ :method => :post }, { :status => 400 })
|
296
|
+
end
|
297
|
+
after do
|
298
|
+
Excon.stubs.shift
|
299
|
+
Docker.options = {}
|
300
|
+
end
|
289
301
|
|
290
302
|
it 'raises an error' do
|
291
303
|
expect { subject.create }.to raise_error(Docker::Error::ClientError)
|
@@ -329,8 +341,14 @@ describe Docker::Container do
|
|
329
341
|
subject { described_class }
|
330
342
|
|
331
343
|
context 'when the HTTP response is not a 200' do
|
332
|
-
before
|
333
|
-
|
344
|
+
before do
|
345
|
+
Docker.options = { :mock => true }
|
346
|
+
Excon.stub({ :method => :get }, { :status => 500 })
|
347
|
+
end
|
348
|
+
after do
|
349
|
+
Excon.stubs.shift
|
350
|
+
Docker.options = {}
|
351
|
+
end
|
334
352
|
|
335
353
|
it 'raises an error' do
|
336
354
|
expect { subject.all }
|