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.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/.cane +0 -1
  3. data/README.md +6 -7
  4. data/docker-api.gemspec +3 -4
  5. data/lib/docker.rb +10 -5
  6. data/lib/docker/connection.rb +2 -0
  7. data/lib/docker/container.rb +39 -4
  8. data/lib/docker/error.rb +6 -0
  9. data/lib/docker/event.rb +40 -0
  10. data/lib/docker/image.rb +37 -13
  11. data/lib/docker/util.rb +25 -1
  12. data/lib/docker/version.rb +2 -2
  13. data/spec/docker/container_spec.rb +37 -19
  14. data/spec/docker/event_spec.rb +79 -0
  15. data/spec/docker/image_spec.rb +89 -30
  16. data/spec/docker/util_spec.rb +83 -0
  17. data/spec/docker_spec.rb +47 -2
  18. data/spec/fixtures/{Dockerfile → build_from_dir/Dockerfile} +0 -0
  19. data/spec/fixtures/top/Dockerfile +2 -0
  20. data/spec/support/vcr.rb +1 -4
  21. data/spec/vcr/Docker/_authenticate_/with_valid_credentials/logs_in_and_sets_the_creds.yml +33 -0
  22. data/spec/vcr/Docker/_info/returns_the_info_as_a_Hash.yml +7 -7
  23. data/spec/vcr/Docker/_validate_version/when_nothing_is_raised/validate_version_/.yml +6 -6
  24. data/spec/vcr/Docker/_version/returns_the_version_as_a_Hash.yml +6 -6
  25. data/spec/vcr/Docker_Container/_all/when_the_HTTP_response_is_a_200/materializes_each_Container_into_a_Docker_Container.yml +27 -97
  26. data/spec/vcr/Docker_Container/_attach/yields_each_chunk.yml +18 -19
  27. data/spec/vcr/Docker_Container/_changes/returns_the_changes_as_an_array.yml +22 -22
  28. data/spec/vcr/Docker_Container/_commit/creates_a_new_Image_from_the_Container_s_changes.yml +18 -18
  29. data/spec/vcr/Docker_Container/_commit/if_run_is_passed_it_saves_the_command_in_the_image/saves_the_command.yml +178 -0
  30. data/spec/vcr/Docker_Container/_copy/when_the_file_does_not_exist/raises_an_error.yml +54 -24
  31. data/spec/vcr/Docker_Container/_copy/when_the_input_is_a_directory/yields_each_chunk_of_the_tarred_directory.yml +27 -60
  32. data/spec/vcr/Docker_Container/_copy/when_the_input_is_a_file/yields_each_chunk_of_the_tarred_file.yml +27 -60
  33. 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
  34. data/spec/vcr/Docker_Container/_delete/deletes_the_container.yml +16 -46
  35. data/spec/vcr/Docker_Container/_export/yields_each_chunk.yml +24 -17
  36. data/spec/vcr/Docker_Container/_json/returns_the_description_as_a_Hash.yml +14 -14
  37. data/spec/vcr/Docker_Container/_kill/kills_the_container.yml +36 -49
  38. data/spec/vcr/Docker_Container/_restart/restarts_the_container.yml +43 -43
  39. data/spec/vcr/Docker_Container/_run/when_the_Container_s_command_does_not_return_status_code_of_0/raises_an_error.yml +17 -17
  40. 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
  41. data/spec/vcr/Docker_Container/_start/starts_the_container.yml +25 -25
  42. data/spec/vcr/Docker_Container/_stop/stops_the_container.yml +40 -53
  43. data/spec/vcr/Docker_Container/_top/returns_the_top_commands_as_an_Array.yml +89 -25
  44. data/spec/vcr/Docker_Container/_wait/waits_for_the_command_to_finish.yml +17 -17
  45. data/spec/vcr/Docker_Container/_wait/when_an_argument_is_given/and_a_command_runs_for_too_long/raises_a_ServerError.yml +12 -12
  46. data/spec/vcr/Docker_Container/_wait/when_an_argument_is_given/sets_the_read_timeout_to_that_amount_of_time.yml +17 -17
  47. data/spec/vcr/Docker_Image/_all/materializes_each_Image_into_a_Docker_Image.yml +12 -12
  48. 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
  49. data/spec/vcr/Docker_Image/_build/with_a_valid_Dockerfile/{builds_an_image.yml → without_query_parameters/builds_an_image.yml} +5 -5
  50. data/spec/vcr/Docker_Image/_build/with_an_invalid_Dockerfile/throws_a_UnexpectedResponseError.yml +7 -9
  51. data/spec/vcr/Docker_Image/_build_from_dir/with_a_valid_Dockerfile/with_no_query_parameters/builds_the_image.yml +121 -0
  52. 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
  53. 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
  54. data/spec/vcr/Docker_Image/_history/returns_the_history_of_the_Image.yml +10 -10
  55. data/spec/vcr/Docker_Image/_insert/inserts_the_url_s_file_into_a_new_Image.yml +53 -109
  56. data/spec/vcr/Docker_Image/_insert_local/when_the_local_file_does_exist/creates_a_new_Image_that_has_that_file.yml +32 -68
  57. data/spec/vcr/Docker_Image/_insert_local/when_the_local_file_does_not_exist/raises_an_error.yml +5 -5
  58. data/spec/vcr/Docker_Image/_insert_local/when_there_are_multiple_files_passed/creates_a_new_Image_that_has_each_file.yml +40 -68
  59. data/spec/vcr/Docker_Image/_json/returns_additional_information_about_image_image.yml +10 -10
  60. data/spec/vcr/Docker_Image/_push/pushes_the_Image.yml +161 -0
  61. data/spec/vcr/Docker_Image/_remove/removes_the_Image.yml +17 -17
  62. 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
  63. data/spec/vcr/Docker_Image/_run/when_the_argument_is_an_Array/creates_a_new_Container.yml +23 -24
  64. 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
  65. 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
  66. data/spec/vcr/Docker_Image/_search/materializes_each_Image_into_a_Docker_Image.yml +13 -11
  67. data/spec/vcr/Docker_Image/_tag/tags_the_image_with_the_repo_name.yml +10 -10
  68. metadata +32 -24
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 73fb37b59d2a47b3db7d9c8a380f6e62ebdff7eb
4
- data.tar.gz: eb221917606fb54355f075be67c508597749c67e
3
+ metadata.gz: a4a13f5948a8df770804ade2a22dc3c31e7fc1b1
4
+ data.tar.gz: c3ac3bb3d1219fb34fbb1e91b4ee63e7fc4486b9
5
5
  SHA512:
6
- metadata.gz: 089db48c5b0bf31aab2d7a2136ea0a26818a93faa41e27ec1e838037cdf94eb7873b1b099a6cfd6a6f942de7c2bddf356e412a6417a1c66f3a0defebe2c6c9e1
7
- data.tar.gz: e6aa7b45e53e29e09130bf3f39722b5ec8eb8c537e6a01244aaf0411c65b729d38ee4603f7e7be28cb4b665feda025cf1f73e49e9e4b5b3f2be8b7a03585891b
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 :stdout are set.
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' => %[find / -name *])
236
- container.tap(&:start).attach { |chunk| puts chunk }
237
- # => nil
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.27.3'
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 'webmock', '>= 1.11.0'
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
- attr_reader :creds
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
- @creds = options.to_json
60
- connection.post(:path => '/auth', :body => @creds)
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!, :version,
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'
@@ -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
@@ -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 = { :stream => true, :stdout => true }.merge(options)
54
- connection.post(path_for(:attach), opts, :response_block => block)
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
- hash = Docker::Util.parse_json(connection.post('/commit', options))
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
- private :path_for
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
@@ -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
- def run(cmd)
20
- cmd = cmd.split(/\s+/) if cmd.is_a?(String)
21
- Docker::Container.create({ 'Image' => self.id, 'Cmd' => cmd }, connection)
22
- .tap(&:start!)
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
- connection.post(path_for(:push), options, :body => Docker.creds)
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
- instance = new(conn)
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
@@ -1,7 +1,7 @@
1
1
  module Docker
2
2
  # The version of the docker-api gem.
3
- VERSION = '1.6.0'
3
+ VERSION = '1.7.0'
4
4
 
5
5
  # The version of the compatible Docker remote API.
6
- API_VERSION = '1.4'
6
+ API_VERSION = '1.6'
7
7
  end
@@ -58,16 +58,14 @@ describe Docker::Container do
58
58
  end
59
59
 
60
60
  describe '#top' do
61
- subject {
62
- described_class.create(
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(:top) { subject.top }
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
- before { subject.start }
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.start.wait }
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 = chunk
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
- subject.attach { |chunk|
137
- chunk.should == "/\n"
138
- break
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 { Excon.stub({ :method => :post }, { :status => 400 }) }
288
- after { Excon.stubs.shift }
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 { Excon.stub({ :method => :get }, { :status => 500 }) }
333
- after { Excon.stubs.shift }
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 }