docker-client 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. data/.gitignore +17 -0
  2. data/.rspec +2 -0
  3. data/.rvmrc +1 -0
  4. data/Gemfile +4 -0
  5. data/Guardfile +9 -0
  6. data/LICENSE.txt +22 -0
  7. data/MIT-LICENSE +21 -0
  8. data/README.md +87 -0
  9. data/Rakefile +15 -0
  10. data/docker.gemspec +40 -0
  11. data/lib/docker.rb +10 -0
  12. data/lib/docker/api.rb +32 -0
  13. data/lib/docker/connection.rb +83 -0
  14. data/lib/docker/error.rb +9 -0
  15. data/lib/docker/error/bad_parameter_error.rb +7 -0
  16. data/lib/docker/error/container_not_found.rb +7 -0
  17. data/lib/docker/error/internal_server_error.rb +7 -0
  18. data/lib/docker/error/not_found_error.rb +7 -0
  19. data/lib/docker/resource.rb +8 -0
  20. data/lib/docker/resource/base.rb +23 -0
  21. data/lib/docker/resource/container.rb +131 -0
  22. data/lib/docker/resource/image.rb +10 -0
  23. data/lib/docker/version.rb +3 -0
  24. data/spec/cassettes/Docker_Connection/returns_a_valid_response_for_a_basic_request.yml +40 -0
  25. data/spec/cassettes/Docker_Connection/returns_a_valid_response_for_get_request.yml +105 -0
  26. data/spec/cassettes/Docker_Connection/returns_a_valid_response_for_post_request.yml +30 -0
  27. data/spec/cassettes/Docker_Connection/returns_status_404_for_non_existent_path.yml +31 -0
  28. data/spec/cassettes/Docker_Connection/sets_given_query_parameters.yml +31 -0
  29. data/spec/cassettes/Docker_Connection/sets_given_request_headers.yml +33 -0
  30. data/spec/cassettes/Docker_Resource_Container/changes/inspects_the_container_s_filesystem_changes.yml +33 -0
  31. data/spec/cassettes/Docker_Resource_Container/create/raises_an_exception_when_called_with_invalid_options.yml +34 -0
  32. data/spec/cassettes/Docker_Resource_Container/create/with_many_settings.yml +36 -0
  33. data/spec/cassettes/Docker_Resource_Container/create/with_minimal_settings.yml +34 -0
  34. data/spec/cassettes/Docker_Resource_Container/kill/raises_an_exception_for_an_unknow_container.yml +31 -0
  35. data/spec/cassettes/Docker_Resource_Container/kill/the_container.yml +30 -0
  36. data/spec/cassettes/Docker_Resource_Container/lists/all_running_containers.yml +46 -0
  37. data/spec/cassettes/Docker_Resource_Container/lists/limit_last_created_containers.yml +34 -0
  38. data/spec/cassettes/Docker_Resource_Container/lists/non-running_processes_too.yml +114 -0
  39. data/spec/cassettes/Docker_Resource_Container/lists/processes_before_a_certain_created_container.yml +110 -0
  40. data/spec/cassettes/Docker_Resource_Container/lists/processes_since_a_certain_created_container.yml +34 -0
  41. data/spec/cassettes/Docker_Resource_Container/logs/returns_the_stderr_of_a_container.yml +25 -0
  42. data/spec/cassettes/Docker_Resource_Container/logs/returns_the_stdout_of_a_container.yml +25 -0
  43. data/spec/cassettes/Docker_Resource_Container/remove/deletes_the_container.yml +30 -0
  44. data/spec/cassettes/Docker_Resource_Container/remove/raises_an_exception_with_an_invalid_container.yml +31 -0
  45. data/spec/cassettes/Docker_Resource_Container/restarts/raises_an_exception_for_an_unknown_container.yml +31 -0
  46. data/spec/cassettes/Docker_Resource_Container/restarts/the_container.yml +30 -0
  47. data/spec/cassettes/Docker_Resource_Container/shows/the_low_level_details.yml +48 -0
  48. data/spec/cassettes/Docker_Resource_Container/start/brings_a_container_into_state_running.yml +30 -0
  49. data/spec/cassettes/Docker_Resource_Container/start/raises_an_exception_for_an_unknown_container.yml +31 -0
  50. data/spec/cassettes/Docker_Resource_Container/stop/halts_a_container.yml +30 -0
  51. data/spec/cassettes/Docker_Resource_Container/stop/raises_an_exception_for_an_unknown_container.yml +31 -0
  52. data/spec/cassettes/Docker_Resource_Container/wait/blocks_until_the_container_stops.yml +31 -0
  53. data/spec/cassettes/Docker_Resource_Container/wait/raises_an_exception_for_an_unknown_container.yml +31 -0
  54. data/spec/cassettes/test_setup/create_container_connection_post.yml +33 -0
  55. data/spec/cassettes/test_setup/create_container_container_changes.yml +33 -0
  56. data/spec/cassettes/test_setup/create_container_container_kill.yml +34 -0
  57. data/spec/cassettes/test_setup/create_container_container_lists1.yml +34 -0
  58. data/spec/cassettes/test_setup/create_container_container_lists2.yml +33 -0
  59. data/spec/cassettes/test_setup/create_container_container_logs.yml +33 -0
  60. data/spec/cassettes/test_setup/create_container_container_remove.yml +33 -0
  61. data/spec/cassettes/test_setup/create_container_container_restarts.yml +34 -0
  62. data/spec/cassettes/test_setup/create_container_container_shows.yml +33 -0
  63. data/spec/cassettes/test_setup/create_container_container_start.yml +33 -0
  64. data/spec/cassettes/test_setup/create_container_container_stop.yml +34 -0
  65. data/spec/cassettes/test_setup/create_container_container_wait.yml +33 -0
  66. data/spec/cassettes/test_setup/delete_container.yml +405 -0
  67. data/spec/cassettes/test_setup/start_container.yml +235 -0
  68. data/spec/cassettes/test_setup/wait_on_container.yml +63 -0
  69. data/spec/docker/api_spec.rb +37 -0
  70. data/spec/docker/connection_spec.rb +81 -0
  71. data/spec/docker/resource/container_spec.rb +288 -0
  72. data/spec/helpers.rb +48 -0
  73. data/spec/spec_helper.rb +31 -0
  74. metadata +361 -0
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use ruby-1.9.3-p327@octo
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in docker.gemspec
4
+ gemspec
@@ -0,0 +1,9 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard :rspec, :cli => "-t ~live" do
5
+ watch(%r{^spec/.+_spec\.rb$})
6
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
7
+ watch('spec/spec_helper.rb') { "spec" }
8
+ end
9
+
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Georg Kunz
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2013 Georg Kunz.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,87 @@
1
+ # Docker Client
2
+
3
+ Docker client library to access the Docker remote API.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'docker-client'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install docker-client
18
+
19
+ ## Usage
20
+
21
+ So far only the containers resource is supported. The images resource and endpoints in category Mics according to the Docker [Remote API documentation](http://docs.docker.io/en/latest/api/docker_remote_api.html) are not yet implemented.
22
+
23
+
24
+ ````ruby
25
+ require 'docker'
26
+ require 'awesome_print'
27
+
28
+ docker = Docker::API.new(base_url: 'http://localhost:4243')
29
+ containers = docker.containers
30
+
31
+ # Create a new container
32
+ result = containers.create(['/bin/sh', '-c', 'while true; do echo hello world; sleep 1; done'], 'base')
33
+ container_id = result["Id"]
34
+ ap result
35
+
36
+ # Start created container
37
+ containers.start(container_id)
38
+
39
+ # Get container details (inspect)
40
+ details = containers.show(container_id)
41
+ ap details
42
+
43
+ # Get file system changes of container
44
+ changes = containers.changes(container_id)
45
+ ap changes
46
+
47
+ # Attach to container for 3 seconds
48
+ options = {stdout: true, stderr: false}
49
+ containers.attach(container_id, options, 3) do |data|
50
+ puts ">> #{data}"
51
+ end
52
+
53
+ # Get all output since container is started
54
+ output = containers.logs(container_id)
55
+ ap output
56
+
57
+ # List all running containers
58
+ running_containers = containers.list
59
+ ap running_containers
60
+
61
+ # Stop container
62
+ containers.stop(container_id)
63
+
64
+ # Remove container
65
+ containers.remove(container_id)
66
+
67
+ ````
68
+
69
+ ## Development
70
+
71
+ ### Run tests
72
+
73
+ All tests are stubbed with VCR. You can edit the setting `config.default_cassette_options` in `spec_helper.rb` to run the tests against the docker API. Set it to `{:record => :all}`. This will alos re-record all VCR request/response. To run the tests stubbed again uncomment the before mentioned setting.
74
+
75
+ ## Contributing
76
+
77
+ 1. Fork it
78
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
79
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
80
+ 4. Push to the branch (`git push origin my-new-feature`)
81
+ 5. Create new Pull Request
82
+
83
+
84
+ ## License
85
+
86
+ MIT License. Copyright 2013 Georg Kunz.
87
+
@@ -0,0 +1,15 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec) do |t|
5
+ # Documentation output
6
+ # Disable live tests
7
+ t.rspec_opts = "-f d -t ~live"
8
+ end
9
+
10
+ # Only run focus examples
11
+ RSpec::Core::RakeTask.new(:focus) do |t|
12
+ t.rspec_opts = "-f d -t focus"
13
+ end
14
+
15
+ task :default => :spec
@@ -0,0 +1,40 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'docker/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'docker-client'
8
+ spec.version = Docker::VERSION
9
+ spec.authors = ['Georg Kunz']
10
+ spec.email = ['kwd@gmx.ch']
11
+ spec.description = %q{Docker client}
12
+ spec.summary = %q{Docker client library to access the Docker remote API.}
13
+ spec.homepage = 'https://github.com/geku/docker-client'
14
+ spec.license = 'MIT'
15
+ spec.required_ruby_version = '>= 1.9'
16
+
17
+ spec.files = `git ls-files`.split($/)
18
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
+ spec.require_paths = ['lib']
21
+
22
+ spec.add_development_dependency 'bundler', '~> 1.3'
23
+ spec.add_development_dependency 'rake'
24
+ spec.add_development_dependency 'rspec', '~> 2.13'
25
+ spec.add_development_dependency 'vcr', '~> 2.5'
26
+ spec.add_development_dependency 'webmock', '~> 1.11'
27
+ spec.add_development_dependency 'activesupport', '~> 3.2.13'
28
+
29
+ spec.add_runtime_dependency 'multi_json'
30
+ spec.add_runtime_dependency 'json'
31
+ spec.add_runtime_dependency 'curb'
32
+ spec.add_runtime_dependency 'guard'
33
+ spec.add_runtime_dependency 'guard-rspec'
34
+ spec.add_runtime_dependency 'awesome_print'
35
+
36
+ # interesting GEMs
37
+ # https://github.com/intridea/hashie
38
+ # https://github.com/tcocca/rash
39
+
40
+ end
@@ -0,0 +1,10 @@
1
+ require 'active_support/core_ext/numeric'
2
+
3
+ require 'docker/version'
4
+ require 'docker/api'
5
+ require 'docker/resource'
6
+ require 'docker/error'
7
+
8
+ module Docker
9
+ # Your code goes here...
10
+ end
@@ -0,0 +1,32 @@
1
+ require 'docker/connection'
2
+
3
+ module Docker
4
+ end
5
+
6
+ class Docker::API
7
+
8
+ attr_reader :connection
9
+
10
+ def initialize(options)
11
+ @debug = options[:debug]
12
+ @ssl = options[:ssl] || { :verify => false }
13
+ base_url = options[:base_url]
14
+ @connection = Docker::Connection.new(base_url: base_url)
15
+ # @faraday_adapter = options[:faraday_adapter] || Faraday.default_adapter
16
+ # @faraday = options[:faraday] || default_faraday
17
+ end
18
+
19
+ def containers
20
+ Docker::Resource::Container.new(@connection)
21
+ end
22
+
23
+ def images
24
+ Docker::Resource::Image.new(@connection)
25
+ end
26
+
27
+ def system
28
+
29
+ end
30
+
31
+
32
+ end
@@ -0,0 +1,83 @@
1
+ require 'curb'
2
+ require 'multi_json'
3
+
4
+ module Docker
5
+ end
6
+
7
+ class Docker::Connection
8
+
9
+ class Response < Struct.new(:body, :status, :content_type, :timeout)
10
+ def body_as_json
11
+ MultiJson.load(body)
12
+ end
13
+ end
14
+
15
+ def initialize(options = {})
16
+ @curl = Curl::Easy.new
17
+ @base_url = options[:base_url]
18
+ raise(ArgumentError, ':base_url missing') unless @base_url
19
+ end
20
+
21
+ def get(path, params = {}, headers = {})
22
+ resp = perform_request(:GET, path, params, nil, headers)
23
+ raise(Docker::Error::InternalServerError, resp.body) if resp.status == 500
24
+ resp
25
+ end
26
+
27
+ def post(path, params = {}, body = '', headers = {})
28
+ resp = perform_request(:POST, path, params, body, headers)
29
+ raise(Docker::Error::InternalServerError, resp.body) if resp.status == 500
30
+ resp
31
+ end
32
+
33
+ def delete(path, params = {}, headers = {})
34
+ resp = perform_request(:DELETE, path, params, nil, headers)
35
+ raise(Docker::Error::InternalServerError, resp.body) if resp.status == 500
36
+ resp
37
+ end
38
+
39
+ def stream(path, params = {}, timeout = nil, headers = {}, &block)
40
+ raise(ArgumentError, 'Block required to handle streaming response') if block.nil?
41
+ begin
42
+ timeout_raised = false
43
+ set_url(path, params)
44
+ set_headers(headers)
45
+ @curl.timeout = timeout if timeout
46
+ @curl.on_body {|data| block.call(data); data.size }
47
+ @curl.http(:POST)
48
+ rescue Curl::Err::TimeoutError => e
49
+ timeout_raised = true
50
+ end
51
+ Response.new(@curl.body_str, @curl.response_code, @curl.content_type, timeout_raised)
52
+ end
53
+
54
+
55
+ private
56
+
57
+ def perform_request(verb, path, query_params, body, headers)
58
+ set_url(path, query_params)
59
+ set_headers(headers)
60
+ set_body(body)
61
+ @curl.http(verb)
62
+ Response.new(@curl.body_str, @curl.response_code, @curl.content_type)
63
+ end
64
+
65
+ def set_body(body)
66
+ @curl.post_body = body if body
67
+ end
68
+
69
+ def set_url(path, query_params)
70
+ params = query_params.collect do |key, value|
71
+ "#{@curl.escape(key)}=#{@curl.escape(value)}"
72
+ end
73
+ param_str = params.empty? ? '' : "?#{params.join('&')}"
74
+ @curl.url = "#{@base_url}#{path}#{param_str}"
75
+ end
76
+
77
+ def set_headers(headers)
78
+ headers.each do |key, value|
79
+ @curl.headers[key] = value
80
+ end
81
+ end
82
+
83
+ end
@@ -0,0 +1,9 @@
1
+ require 'docker/error/internal_server_error'
2
+ require 'docker/error/container_not_found'
3
+ require 'docker/error/not_found_error'
4
+ require 'docker/error/bad_parameter_error'
5
+
6
+ module Docker
7
+ module Error
8
+ end
9
+ end
@@ -0,0 +1,7 @@
1
+ module Docker
2
+ module Error
3
+ end
4
+ end
5
+
6
+ class Docker::Error::BadParameterError < StandardError
7
+ end
@@ -0,0 +1,7 @@
1
+ module Docker
2
+ module Error
3
+ end
4
+ end
5
+
6
+ class Docker::Error::ContainerNotFound < StandardError
7
+ end
@@ -0,0 +1,7 @@
1
+ module Docker
2
+ module Error
3
+ end
4
+ end
5
+
6
+ class Docker::Error::InternalServerError < StandardError
7
+ end
@@ -0,0 +1,7 @@
1
+ module Docker
2
+ module Error
3
+ end
4
+ end
5
+
6
+ class Docker::Error::NotFoundError < StandardError
7
+ end
@@ -0,0 +1,8 @@
1
+ require 'docker/resource/base'
2
+ require 'docker/resource/container'
3
+ require 'docker/resource/image'
4
+
5
+ module Docker
6
+ module Resource
7
+ end
8
+ end
@@ -0,0 +1,23 @@
1
+ module Docker
2
+ module Resource
3
+ end
4
+ end
5
+
6
+ class Docker::Resource::Base
7
+ def initialize(connection)
8
+ @connection = connection
9
+ end
10
+
11
+ private
12
+
13
+ # TODO add all methods to gather all HTTP calls in this superclass
14
+ # Than it should be easier to replaces it for streaming support
15
+ def get
16
+
17
+ end
18
+
19
+
20
+
21
+ end
22
+
23
+
@@ -0,0 +1,131 @@
1
+
2
+ module Docker
3
+ module Resource
4
+ end
5
+ end
6
+
7
+ class Docker::Resource::Container < Docker::Resource::Base
8
+ # TODO set 'Content-Type: application/json'
9
+
10
+ # Options
11
+ # all
12
+ # limit
13
+ # since
14
+ # before
15
+ def list(options = {})
16
+ @connection.get('/containers/ps', options).body_as_json
17
+ end
18
+
19
+ def create(command, image = 'base', options = {})
20
+ command = [command] if command.is_a?(String)
21
+ body = {'Cmd' => command, 'Image' => image}
22
+ body = options.merge(body)
23
+ json_body = MultiJson.dump(body)
24
+
25
+ response = @connection.post("/containers/create", {}, json_body, {'Content-Type' => 'application/json'})
26
+ raise(Docker::Error::NotFoundError) if response.status == 404
27
+ response.body_as_json
28
+ end
29
+
30
+ # inspect is a Ruby internal method that should not be overwritten
31
+ # therefore we use show as it displays the container details
32
+ def show(container_id)
33
+ @connection.get("/containers/#{container_id}/json").body_as_json
34
+ end
35
+
36
+ def changes(container_id)
37
+ @connection.get("/containers/#{container_id}/changes").body_as_json
38
+ end
39
+
40
+ # Returns a stream
41
+ def export
42
+
43
+ end
44
+
45
+ def start(container_id)
46
+ status = @connection.post("/containers/#{container_id}/start").status
47
+ raise_if_container_not_found(status)
48
+ status == 204
49
+ end
50
+
51
+ def stop(container_id, timeout = nil)
52
+ params = {}
53
+ params['t'] = timeout if timeout
54
+ status = @connection.post("/containers/#{container_id}/stop", params).status
55
+ raise_if_container_not_found(status)
56
+ status == 204
57
+ end
58
+
59
+ def restart(container_id, timeout = nil)
60
+ params = {}
61
+ params['t'] = timeout if timeout
62
+ status = @connection.post("/containers/#{container_id}/restart", params).status
63
+ raise_if_container_not_found(status)
64
+ status == 204
65
+ end
66
+
67
+ def kill(container_id)
68
+ status = @connection.post("/containers/#{container_id}/kill").status
69
+ raise_if_container_not_found(status)
70
+ status == 204
71
+ end
72
+
73
+ # Valid options:
74
+ # stdout true default is false
75
+ # stderr true default is false
76
+ def attach(container_id, options = {}, timeout = nil, &block)
77
+ raise(ArgumentError, "Block must be given to handle streamed data") if block.nil?
78
+ options = {stdout: true, stderr: true} if options.empty?
79
+ options = options.merge(stream: true, logs: false)
80
+
81
+ response = @connection.stream("/containers/#{container_id}/attach", options, timeout, {}, &block)
82
+ raise_if_container_not_found(response.status)
83
+ raise(BadParameterError) if response.status == 400
84
+ response
85
+ end
86
+
87
+ def logs(container_id, options = {})
88
+ options = {stdout: true, stderr: true} if options.empty?
89
+ options = options.merge(logs: true, stream: false)
90
+
91
+ response = @connection.post("/containers/#{container_id}/attach", options)
92
+ raise_if_container_not_found(response.status)
93
+ raise(BadParameterError) if response.status == 400
94
+ response.body
95
+ end
96
+
97
+ # Blocks until container exits
98
+ def wait(container_id)
99
+ response = @connection.post("/containers/#{container_id}/wait")
100
+ raise_if_container_not_found(response.status)
101
+ response.body_as_json
102
+ end
103
+
104
+ # Options:
105
+ # v remove volumes of container
106
+ def remove(container_id, delete_volumes = false)
107
+ params = {v: delete_volumes}
108
+ status = @connection.delete("/containers/#{container_id}", params).status
109
+ raise_if_container_not_found(status)
110
+ status == 204
111
+ end
112
+
113
+ private
114
+
115
+ def raise_if_container_not_found(status)
116
+ raise(Docker::Error::ContainerNotFound) if status == 404
117
+ end
118
+
119
+
120
+ end
121
+
122
+
123
+
124
+
125
+
126
+
127
+
128
+
129
+
130
+
131
+