docker-api 0.1.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. data/README.md +21 -48
  2. data/lib/docker/connection.rb +8 -1
  3. data/lib/docker/container.rb +11 -12
  4. data/lib/docker/error.rb +0 -4
  5. data/lib/docker/image.rb +44 -30
  6. data/lib/docker/model.rb +30 -40
  7. data/lib/docker/version.rb +1 -1
  8. data/spec/docker/connection_spec.rb +3 -2
  9. data/spec/docker/container_spec.rb +206 -377
  10. data/spec/docker/image_spec.rb +123 -237
  11. data/spec/vcr/Docker_Container/_all/when_the_HTTP_response_is_a_200/materializes_each_Container_into_a_Docker_Container.yml +2503 -263
  12. data/spec/vcr/Docker_Container/_attach/{when_the_Container_has_been_created/when_the_HTTP_response_status_is_200 → when_the_HTTP_response_status_is_200}/yields_each_chunk.yml +30 -22
  13. data/spec/vcr/Docker_Container/_changes/{when_the_Container_has_been_created/when_the_HTTP_response_status_is_200 → when_the_HTTP_response_status_is_200}/returns_the_changes_as_an_array.yml +41 -33
  14. data/spec/vcr/Docker_Container/_commit/{when_the_Container_has_been_created/when_the_HTTP_response_status_is_200 → when_the_HTTP_response_status_is_200}/creates_a_new_Image_from_the_Container_s_changes.yml +34 -28
  15. data/spec/vcr/Docker_Container/{_create_ → _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 +11 -9
  16. data/spec/vcr/Docker_Container/_export/{when_the_Container_has_been_created/when_the_HTTP_response_status_is_200 → when_the_HTTP_response_status_is_200}/yields_each_chunk.yml +43 -29
  17. data/spec/vcr/Docker_Container/_json/when_the_HTTP_response_status_is_200/returns_the_description_as_a_Hash.yml +63 -0
  18. data/spec/vcr/Docker_Container/_kill/when_the_HTTP_response_status_is_204/kills_the_container.yml +1358 -0
  19. data/spec/vcr/Docker_Container/_restart/{when_the_Container_has_been_created/when_the_HTTP_response_status_is_204 → when_the_HTTP_response_status_is_204}/restarts_the_container.yml +73 -59
  20. data/spec/vcr/Docker_Container/_start/{when_the_Container_has_been_created/when_the_HTTP_response_status_is_200 → when_the_HTTP_response_status_is_200}/starts_the_container.yml +33 -28
  21. data/spec/vcr/Docker_Container/_stop/when_the_HTTP_response_status_is_204/stops_the_container.yml +1387 -0
  22. data/spec/vcr/Docker_Container/_wait/{when_the_Container_has_been_created/when_the_HTTP_response_status_is_200 → when_the_HTTP_response_status_is_200}/waits_for_the_command_to_finish.yml +31 -25
  23. data/spec/vcr/Docker_Image/_all/when_the_HTTP_response_is_a_200/materializes_each_Image_into_a_Docker_Image.yml +6 -6
  24. data/spec/vcr/Docker_Image/_build/with_a_valid_Dockerfile/builds_an_image.yml +4 -4
  25. data/spec/vcr/Docker_Image/_build/with_an_invalid_Dockerfile/throws_a_UnexpectedResponseError.yml +4 -4
  26. data/spec/vcr/Docker_Image/_build_from_dir/with_a_valid_Dockerfile/builds_the_image.yml +14 -14
  27. data/spec/vcr/Docker_Image/_create/when_the_Image_does_not_yet_exist_and_the_body_is_a_Hash/when_the_HTTP_request_returns_a_200/sets_the_id.yml +33 -0
  28. data/spec/vcr/Docker_Image/_history/when_the_HTTP_response_status_is_200/returns_the_history_of_the_Image.yml +63 -0
  29. data/spec/vcr/Docker_Image/_import/when_the_file_does_exist/creates_the_Image.yml +11 -9
  30. data/spec/vcr/Docker_Image/_insert/when_the_HTTP_response_status_is_200/inserts_the_url_s_file_into_a_new_Image.yml +220 -0
  31. data/spec/vcr/Docker_Image/_json/when_the_HTTP_response_status_is_200/returns_additional_information_about_image_image.yml +63 -0
  32. data/spec/vcr/Docker_Image/_remove/when_the_HTTP_response_status_is_204/removes_the_Image.yml +93 -0
  33. data/spec/vcr/Docker_Image/_remove/when_the_Image_has_been_created/when_the_HTTP_response_status_is_204/removes_the_Image.yml +63 -0
  34. data/spec/vcr/Docker_Image/_run/when_the_Image_has_been_created/when_the_argument_is_a_String/splits_the_String_by_spaces_and_creates_a_new_Container.yml +119 -0
  35. data/spec/vcr/Docker_Image/_run/when_the_Image_has_been_created/when_the_argument_is_an_Array/creates_a_new_Container.yml +119 -0
  36. data/spec/vcr/Docker_Image/_run/when_the_argument_is_a_String/splits_the_String_by_spaces_and_creates_a_new_Container.yml +119 -0
  37. data/spec/vcr/Docker_Image/_run/when_the_argument_is_an_Array/creates_a_new_Container.yml +119 -0
  38. data/spec/vcr/Docker_Image/_tag/when_the_HTTP_response_status_is_200/tags_the_image_with_the_repo_name.yml +63 -0
  39. metadata +46 -29
  40. data/spec/vcr/Docker_Container/_json/when_the_Container_has_been_created/when_the_HTTP_response_status_is_200/returns_the_description_as_a_Hash.yml +0 -61
  41. data/spec/vcr/Docker_Container/_kill/when_the_Container_has_been_created/when_the_HTTP_response_status_is_204/kills_the_container.yml +0 -232
  42. data/spec/vcr/Docker_Container/_stop/when_the_Container_has_been_created/when_the_HTTP_response_status_is_204/stops_the_container.yml +0 -259
data/README.md CHANGED
@@ -70,27 +70,16 @@ Docker.authenticate!('username' => 'docker-fan-boi', 'password' => 'i<3docker',
70
70
  ```
71
71
 
72
72
  ## Images
73
- Just about every method here has a one-to-one mapping with the [Images](http://docs.docker.io/en/latest/api/docker_remote_api_v1.2/#images) section of the API. If an API call accepts query parameters, these can be passed as an Hash to it's corresponding method.
73
+ Just about every method here has a one-to-one mapping with the [Images](http://docs.docker.io/en/latest/api/docker_remote_api_v1.2/#images) section of the API. If an API call accepts query parameters, these can be passed as an Hash to it's corresponding method. Also, note that `Docker::Image.new` is a private method, so you must use `.create`, `.build`, `.build_from_dir`, or `.import` to make an instance.
74
74
 
75
75
  ```ruby
76
76
  require 'docker'
77
77
  # => true
78
78
 
79
- # Instantiate a new Image. Note that this does NOT create the Image.
80
- image = Docker::Image.new
81
- # => Docker::Image { :id => , :connection => Docker::Connection { :url => http://localhost, :options => {:port=>4243} } }
82
-
83
- # Test if the Image is created.
84
- image.created?
85
- # => false
86
-
87
- # Create the Image.
88
- image.create!('fromRepo' => 'base')
79
+ # Create an Image.
80
+ Docker::Image.create('fromRepo' => 'base')
89
81
  # => Docker::Image { :id => ae7ffbcd1, :connection => Docker::Connection { :url => http://localhost, :options => {:port=>4243} } }
90
82
 
91
- image.created?
92
- # => true
93
-
94
83
  # Insert a file into an Image. Returns a new Image that contains that file.
95
84
  image.insert('path' => '/google', 'url' => 'http://google.com')
96
85
  # => Docker::Image { :id => 11ef6c882, :connection => Docker::Connection { :url => http://localhost, :options => {:port=>4243} } }
@@ -112,25 +101,18 @@ image.history
112
101
  image.push
113
102
  # => true
114
103
 
104
+ # Given a command, create a new Container to run that command in the Image.
105
+ image.run('ls -l')
106
+ # => Docker::Container { id => aaef712eda, :connection => Docker::Connection { :url => http://localhost, :options => {:port=>4243} } }
107
+
115
108
  # Remove the Image from the server.
116
109
  image.remove
117
110
  # => true
118
111
 
119
- image.created?
120
- # => false
121
-
122
112
  # Given a Container's export, creates a new Image.
123
- image.create_from_file('some-export.tar')
113
+ Docker::Image.import('some-export.tar')
124
114
  # => Docker::Image { :id => 66b712aef, :connection => Docker::Connection { :url => http://localhost, :options => {:port=>4243} } }
125
115
 
126
- # Load all Images on your Docker server.
127
- Docker::Image.all
128
- # => [Docker::Image { :id => b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc, :connection => Docker::Connection { :url => http://localhost, :options => {:port=>4243} } }, Docker::Image { :id => 8dbd9e392a964056420e5d58ca5cc376ef18e2de93b5cc90e868a1bbc8318c1c, :connection => Docker::Connection { :url => http://localhost, :options => {:port=>4243} } }]
129
-
130
- # Search the Docker registry.
131
- Docker::Image.search('term' => 'sshd')
132
- # => [Docker::Image { :id => cespare/sshd, :connection => Docker::Connection { :url => http://localhost, :options => {:port=>4243} } }, Docker::Image { :id => johnfuller/sshd, :connection => Docker::Connection { :url => http://localhost, :options => {:port=>4243} } }, Docker::Image { :id => dhrp/mongodb-sshd, :connection => Docker::Connection { :url => http://localhost, :options => {:port=>4243} } }, Docker::Image { :id => rayang2004/sshd, :connection => Docker::Connection { :url => http://localhost, :options => {:port=>4243} } }, Docker::Image { :id => dhrp/sshd, :connection => Docker::Connection { :url => http://localhost, :options => {:port=>4243} } }, Docker::Image { :id => toorop/daemontools-sshd, :connection => Docker::Connection { :url => http://localhost, :options => {:port=>4243} } }, Docker::Image { :id => toorop/daemontools-sshd-nginx, :connection => Docker::Connection { :url => http://localhost, :options => {:port=>4243} } }, Docker::Image { :id => toorop/daemontools-sshd-nginx-php-fpm, :connection => Docker::Connection { :url => http://localhost, :options => {:port=>4243} } }, Docker::Image { :id => mbkan/lamp, :connection => Docker::Connection { :url => http://localhost, :options => {:port=>4243} } }, Docker::Image { :id => toorop/golang, :connection => Docker::Connection { :url => http://localhost, :options => {:port=>4243} } }, Docker::Image { :id => wma55/u1210sshd, :connection => Docker::Connection { :url => http://localhost, :options => {:port=>4243} } }, Docker::Image { :id => jdswinbank/sshd, :connection => Docker::Connection { :url => http://localhost, :options => {:port=>4243} } }, Docker::Image { :id => vgauthier/sshd, :connection => Docker::Connection { :url => http://localhost, :options => {:port=>4243} } }]
133
-
134
116
  # Create an Image from a Dockerfile as a String.
135
117
  Docker::Image.build("from base\nrun touch /test")
136
118
  # => Docker::Image { :id => b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc, :connection => Docker::Connection { :url => http://localhost, :options => {:port=>4243} } }
@@ -138,29 +120,26 @@ Docker::Image.build("from base\nrun touch /test")
138
120
  # Create an Image from a Dockerfile.
139
121
  Dockerfile::Image.build_from_dir('.')
140
122
  # => Docker::Image { :id => 1266dc19e, :connection => Docker::Connection { :url => http://localhost, :options => {:port=>4243} } }
123
+
124
+ # Load all Images on your Docker server.
125
+ Docker::Image.all
126
+ # => [Docker::Image { :id => b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc, :connection => Docker::Connection { :url => http://localhost, :options => {:port=>4243} } }, Docker::Image { :id => 8dbd9e392a964056420e5d58ca5cc376ef18e2de93b5cc90e868a1bbc8318c1c, :connection => Docker::Connection { :url => http://localhost, :options => {:port=>4243} } }]
127
+
128
+ # Search the Docker registry.
129
+ Docker::Image.search('term' => 'sshd')
130
+ # => [Docker::Image { :id => cespare/sshd, :connection => Docker::Connection { :url => http://localhost, :options => {:port=>4243} } }, Docker::Image { :id => johnfuller/sshd, :connection => Docker::Connection { :url => http://localhost, :options => {:port=>4243} } }, Docker::Image { :id => dhrp/mongodb-sshd, :connection => Docker::Connection { :url => http://localhost, :options => {:port=>4243} } }, Docker::Image { :id => rayang2004/sshd, :connection => Docker::Connection { :url => http://localhost, :options => {:port=>4243} } }, Docker::Image { :id => dhrp/sshd, :connection => Docker::Connection { :url => http://localhost, :options => {:port=>4243} } }, Docker::Image { :id => toorop/daemontools-sshd, :connection => Docker::Connection { :url => http://localhost, :options => {:port=>4243} } }, Docker::Image { :id => toorop/daemontools-sshd-nginx, :connection => Docker::Connection { :url => http://localhost, :options => {:port=>4243} } }, Docker::Image { :id => toorop/daemontools-sshd-nginx-php-fpm, :connection => Docker::Connection { :url => http://localhost, :options => {:port=>4243} } }, Docker::Image { :id => mbkan/lamp, :connection => Docker::Connection { :url => http://localhost, :options => {:port=>4243} } }, Docker::Image { :id => toorop/golang, :connection => Docker::Connection { :url => http://localhost, :options => {:port=>4243} } }, Docker::Image { :id => wma55/u1210sshd, :connection => Docker::Connection { :url => http://localhost, :options => {:port=>4243} } }, Docker::Image { :id => jdswinbank/sshd, :connection => Docker::Connection { :url => http://localhost, :options => {:port=>4243} } }, Docker::Image { :id => vgauthier/sshd, :connection => Docker::Connection { :url => http://localhost, :options => {:port=>4243} } }]
141
131
  ```
142
132
 
143
133
  ## Containers
144
- Much like the Images, this object also has a one-to-one mapping with the [Containers](http://docs.docker.io/en/latest/api/docker_remote_api_v1.2/#containers) section of the API.
134
+ Much like the Images, this object also has a one-to-one mapping with the [Containers](http://docs.docker.io/en/latest/api/docker_remote_api_v1.2/#containers) section of the API. Also like Images, `.new` is a private method, so you must use `.create` to make an instance.
145
135
 
146
136
  ```ruby
147
137
  require 'docker'
148
138
 
149
- # Instantiate a new Container.
150
- container = Docker::Container.new
151
- # => Docker::Container { :id => , :connection => Docker::Connection { :url => http://localhost, :options => {:port=>4243} } }
152
-
153
- # Test if the has been created.
154
- container.created?
155
- # => false
156
-
157
- # Create the Container.
158
- container.create!('Cmd' => ['ls'], 'Image' => 'base')
139
+ # Create a Container.
140
+ Docker::Container.create('Cmd' => ['ls'], 'Image' => 'base')
159
141
  # => Docker::Container { :id => 492510dd38e4, :connection => Docker::Connection { :url => http://localhost, :options => {:port=>4243} } }
160
142
 
161
- container.created?
162
- # => true
163
-
164
143
  # Get more information about the Container.
165
144
  container.json
166
145
  # => {"Id"=>"492510dd38e4da7703f36dfccd013de672b8250f57f59d1555ced647766b5e82", "Created"=>"2013-06-20T10:46:02.897548-04:00", "Path"=>"ls", "Args"=>[], "Config"=>{"Hostname"=>"492510dd38e4", "User"=>"", "Memory"=>0, "MemorySwap"=>0, "CpuShares"=>0, "AttachStdin"=>false, "AttachStdout"=>false, "AttachStderr"=>false, "PortSpecs"=>nil, "Tty"=>false, "OpenStdin"=>false, "StdinOnce"=>false, "Env"=>nil, "Cmd"=>["ls"], "Dns"=>nil, "Image"=>"base", "Volumes"=>nil, "VolumesFrom"=>""}, "State"=>{"Running"=>false, "Pid"=>0, "ExitCode"=>0, "StartedAt"=>"0001-01-01T00:00:00Z", "Ghost"=>false}, "Image"=>"b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc", "NetworkSettings"=>{"IpAddress"=>"", "IpPrefixLen"=>0, "Gateway"=>"", "Bridge"=>"", "PortMapping"=>nil}, "SysInitPath"=>"/usr/bin/docker", "ResolvConfPath"=>"/etc/resolv.conf", "Volumes"=>nil}
@@ -197,6 +176,7 @@ container.wait
197
176
  # => {'StatusCode'=>0}
198
177
 
199
178
  # Attach to the Container. Currently, the below options are the only valid ones.
179
+ # By default, :stream and :stdout are set.
200
180
  container.attach(:stream => true, :stdout => true, :stderr => true, :logs => true)
201
181
  # => "bin\nboot\ndev\netc\nhome\nlib\nlib64\nmedia\nmnt\nopt\nproc\nroot\nrun\nsbin\nselinux\nsrv\nsys\ntmp\nusr\nvar"
202
182
 
@@ -211,19 +191,12 @@ Docker::Container.all(:all => true)
211
191
 
212
192
  ## Connecting to Multiple Servers
213
193
 
214
- By default, each object connects to the connection specified by `Docker.connection`. If you need to connect to multiple servers, you can do so by specifying the connection on `#new` or in the utilizing class method. Examples:
194
+ By default, each object connects to the connection specified by `Docker.connection`. If you need to connect to multiple servers, you can do so by specifying the connection on `#new` or in the utilizing class method. For example:
215
195
 
216
196
  ```ruby
217
197
  require 'docker'
218
198
 
219
- Docker::Container.new(:connection => Docker::Connection.new(:url => 'http://example.com'))
220
199
  Docker::Container.all({}, Docker::Connection.new(:url => 'http://example.com'))
221
-
222
- Docker::Image.new(:connection => Docker::Connection.new(:url => 'http://example.com'))
223
- Docker::Image.all({}, Docker::Connection.new(:url => 'http://example.com'))
224
- Docker::Image.build('from base', Docker::Connection.new(:url => 'http://example.com'))
225
- Docker::Image.build_from_dir('.', Docker::Connection.new(:url => 'http://example.com'))
226
- Docker::Image.search({ :term => 'sshd' }, Docker::Connection.new(:url => 'http://example.com'))
227
200
  ```
228
201
 
229
202
  ## Known Issues
@@ -20,10 +20,17 @@ class Docker::Connection
20
20
  @resource ||= Excon.new(self.url, self.options)
21
21
  end
22
22
 
23
+ # Nil out the connection. This now happens on every request to prevent socket
24
+ # errors.
25
+ def reset!
26
+ @resource = nil
27
+ end
28
+
23
29
  # Delegate all HTTP methods to the resource.
24
30
  [:get, :put, :post, :delete, :request].each do |method|
25
31
  define_method(method) do |*args, &block|
26
32
  begin
33
+ self.reset!
27
34
  self.resource.public_send(method, *args, &block)
28
35
  rescue Excon::Errors::BadRequest => ex
29
36
  raise ClientError, ex.message
@@ -37,7 +44,7 @@ class Docker::Connection
37
44
  def json_request(method, path, query = {}, &block)
38
45
  params = compile_request_params(method, path, query, &block)
39
46
  body = self.request(params).body
40
- JSON.parse(body) unless (body.nil? || body.empty? || (body == 'null'))
47
+ JSON.parse(body) unless body.nil? || body.empty? || (body == 'null')
41
48
  rescue JSON::ParserError => ex
42
49
  raise UnexpectedResponseError, ex.message
43
50
  end
@@ -13,30 +13,30 @@ class Docker::Container
13
13
  :body => body.to_json,
14
14
  :expects => (200..204)
15
15
  )
16
- self.id = JSON.parse(response.body)['Id']
16
+ @id = JSON.parse(response.body)['Id']
17
17
  self
18
18
  end
19
19
 
20
20
  # Export the Container as a .tgz.
21
- docker_request :export, :get
21
+ get :export
22
22
  # Get more information about the Container.
23
- docker_request :json, :get
23
+ get :json
24
24
  # Wait for the current command to finish executing.
25
- docker_request :wait, :post
25
+ post :wait
26
26
  # Start the Container.
27
- docker_request :start, :post
27
+ post :start
28
28
  # Inspect the Container's changes to the filesysetem
29
- docker_request :changes, :get
29
+ get :changes
30
30
  # Stop the Container.
31
- docker_request :stop, :post
31
+ post :stop
32
32
  # Kill the Container.
33
- docker_request :kill, :post
33
+ post :kill
34
34
  # Restart the Container
35
- docker_request :restart, :post
35
+ post :restart
36
36
 
37
37
  # Attach to a container's standard streams / logs.
38
38
  def attach(options = {})
39
- ensure_created!
39
+ options = { :stream => true, :stdout => true }.merge(options)
40
40
  self.connection.post(
41
41
  :path => "/containers/#{self.id}/attach",
42
42
  :headers => { 'Content-Type' => 'text/plain',
@@ -48,9 +48,8 @@ class Docker::Container
48
48
 
49
49
  # Create an Image from a Container's change.s
50
50
  def commit(options = {})
51
- ensure_created!
52
51
  options.merge!('container' => self.id[0..7])
53
52
  hash = self.connection.json_request(:post, '/commit', options)
54
- Docker::Image.new(:id => hash['Id'], :connection => self.connection)
53
+ Docker::Image.send(:new, :id => hash['Id'], :connection => self.connection)
55
54
  end
56
55
  end
data/lib/docker/error.rb CHANGED
@@ -8,10 +8,6 @@ module Docker::Error
8
8
  # Raised when invalid arguments are passed to a method.
9
9
  class ArgumentError < DockerError; end
10
10
 
11
- # Raised when a method requires a Model to be in a certain state (typically
12
- # created or not created), but the Model is not in that state.
13
- class StateError < DockerError; end
14
-
15
11
  # Raised when a request returns a 400.
16
12
  class ClientError < DockerError; end
17
13
 
data/lib/docker/image.rb CHANGED
@@ -5,13 +5,13 @@ class Docker::Image
5
5
 
6
6
  resource_prefix '/images'
7
7
 
8
- create_request do |options, excon_options|
9
- body = self.connection.post(excon_options.merge(
10
- :path => '/v1.3/images/create',
8
+ create_request do |options|
9
+ body = self.connection.post(
10
+ :path => '/images/create',
11
11
  :headers => { 'User-Agent' => 'Docker-Client/0.4.6' },
12
12
  :query => options,
13
13
  :expects => (200..204)
14
- )).body
14
+ ).body
15
15
  @id = JSON.parse(body)['status'] rescue nil
16
16
  @id ||= options['fromImage']
17
17
  @id ||= "#{options['repo']}/#{options['tag']}"
@@ -19,15 +19,24 @@ class Docker::Image
19
19
  end
20
20
 
21
21
  # Tag the Image.
22
- docker_request :tag, :post
22
+ post :tag
23
23
  # Get more information about the Image.
24
- docker_request :json, :get
24
+ get :json
25
25
  # Get the history of the Image.
26
- docker_request :history, :get
26
+ get :history
27
+
28
+ # Given a command and optional list of streams to attach to, run a command on
29
+ # an Image. This will not modify the Image, but rather create a new Container
30
+ # to run the Image.
31
+ def run(cmd)
32
+ cmd = cmd.split(/\s+/) if cmd.is_a?(String)
33
+ Docker::Container.create({ 'Image' => self.id, 'Cmd' => cmd },
34
+ self.connection)
35
+ .tap(&:start)
36
+ end
27
37
 
28
38
  # Push the Image to the Docker registry.
29
39
  def push(options = {})
30
- ensure_created!
31
40
  self.connection.post(
32
41
  :path => "/images/#{self.id}/push",
33
42
  :headers => { 'Content-Type' => 'text/plain',
@@ -41,7 +50,6 @@ class Docker::Image
41
50
 
42
51
  # Insert a file into the Image, returns a new Image that has that file.
43
52
  def insert(query = {})
44
- ensure_created!
45
53
  body = self.connection.post(
46
54
  :path => "/images/#{self.id}/insert",
47
55
  :headers => { 'Content-Type' => 'text/plain',
@@ -52,16 +60,13 @@ class Docker::Image
52
60
  if (id = body.match(/{"Id":"([a-f0-9]+)"}\z/)).nil? || id[1].empty?
53
61
  raise UnexpectedResponseError, "Could not find Id in '#{body}'"
54
62
  else
55
- Docker::Image.new(:id => id[1], :connection => self.connection)
63
+ self.class.send(:new, :id => id[1], :connection => self.connection)
56
64
  end
57
65
  end
58
66
 
59
67
  # Remove the Image from the server.
60
68
  def remove
61
- ensure_created!
62
69
  self.connection.json_request(:delete, "/images/#{self.id}", nil)
63
- self.id = nil
64
- true
65
70
  end
66
71
 
67
72
  class << self
@@ -74,46 +79,51 @@ class Docker::Image
74
79
  hashes.map { |hash| new(:id => hash['Name'], :connection => connection) }
75
80
  end
76
81
 
82
+ # Import an Image from the output of Docker::Container#export.
77
83
  def import(file, options = {}, connection = Docker.connection)
78
84
  File.open(file, 'r') do |io|
79
- read_chunked = proc { io.read(Excon.defaults[:chunk_size]).to_s }
80
- self.new(:connection => connection)
81
- .create!(options.merge('fromSrc' => '-'),
82
- :request_block => read_chunked)
85
+ body = connection.post(
86
+ :path => '/images/create',
87
+ :headers => { 'User-Agent' => 'Docker-Client/0.4.6',
88
+ 'Transfer-Encoding' => 'chunked' },
89
+ :query => options.merge('fromSrc' => '-'),
90
+ :request_block => proc { io.read(Excon.defaults[:chunk_size]).to_s },
91
+ :expects => (200..204)
92
+ ).body
93
+ new(:id => JSON.parse(body)['status'], :connection => connection)
83
94
  end
84
95
  end
85
96
 
86
97
  # Given a Dockerfile as a string, builds an Image.
87
98
  def build(commands, connection = Docker.connection)
88
99
  body = connection.post(
89
- :path => '/v1.3/build',
100
+ :path => '/build',
90
101
  :body => create_tar(commands),
91
102
  :expects => (200..204)
92
103
  ).body
93
104
  new(:id => extract_id(body), :connection => connection)
94
105
  end
95
106
 
107
+ # Given a directory that contains a Dockerfile, builds an Image.
96
108
  def build_from_dir(dir, connection = Docker.connection)
97
- cwd = FileUtils.pwd
98
- FileUtils.cd(dir)
99
- tar = create_dir_tar('.')
109
+ tar = create_dir_tar(dir)
100
110
  body = connection.post(
101
- :path => '/v1.3/build',
102
- :headers => { 'Content-Type' => 'application/tar',
103
- 'Transfer-Encoding' => 'chunked' },
111
+ :path => '/build',
112
+ :headers => { 'Content-Type' => 'application/tar',
113
+ 'Transfer-Encoding' => 'chunked' },
104
114
  :request_block => proc { tar.read(Excon.defaults[:chunk_size]).to_s },
105
- :expects => (200..204),
115
+ :expects => (200..204),
106
116
  ).body
107
117
  new(:id => extract_id(body), :connection => connection)
108
118
  ensure
109
119
  tar.close
110
- FileUtils.cd(cwd)
111
120
  end
112
121
 
113
122
  private
114
123
  def extract_id(body)
115
- if match = body.lines.to_a[-1].match(/^Successfully built ([a-f0-9]+)$/)
116
- match[1]
124
+ line = body.lines.to_a[-1]
125
+ if (id = line.match(/^Successfully built ([a-f0-9]+)$/)) && !id[1].empty?
126
+ id[1]
117
127
  else
118
128
  raise UnexpectedResponseError, "Couldn't find id: #{body}"
119
129
  end
@@ -129,16 +139,20 @@ class Docker::Image
129
139
  file = File.new('Dockerfile', 'w')
130
140
  file.write(input)
131
141
  file.close
132
- Archive::Tar::Minitar.pack_file("#{path}/Dockerfile", tar)
142
+ Archive::Tar::Minitar.pack_file("Dockerfile", tar)
133
143
  FileUtils.cd(cwd)
134
144
  FileUtils.rm_rf(path)
135
145
  string.tap(&:rewind)
136
146
  end
137
147
 
138
148
  def create_dir_tar(directory)
149
+ cwd = FileUtils.pwd
139
150
  tempfile = File.new('/tmp/out', 'w')
140
- Archive::Tar::Minitar.pack(directory, tempfile)
151
+ FileUtils.cd(directory)
152
+ Archive::Tar::Minitar.pack('.', tempfile)
141
153
  File.new('/tmp/out', 'r')
154
+ ensure
155
+ FileUtils.cd(cwd)
142
156
  end
143
157
  end
144
158
  end
data/lib/docker/model.rb CHANGED
@@ -1,10 +1,16 @@
1
1
  # This module is intended to be used as a Mixin for all objects exposed by the
2
2
  # Remote API. Currently, these are limited to Containers and Images.
3
3
  module Docker::Model
4
+ include Docker::Error
5
+
4
6
  attr_reader :id, :connection
5
7
 
6
8
  def self.included(base)
7
- base.extend(ClassMethods)
9
+ base.class_eval do
10
+ extend ClassMethods
11
+ private_class_method :new, :request, :get, :put, :post, :delete,
12
+ :create_request, :resource_prefix
13
+ end
8
14
  end
9
15
 
10
16
  # Creates a new Model with the specified id and Connection. If a Connection
@@ -12,39 +18,22 @@ module Docker::Model
12
18
  # Docker::Error::ArgumentError is raised.
13
19
  def initialize(options = {})
14
20
  options[:connection] ||= Docker.connection
15
- unless options[:connection].is_a?(Docker::Connection)
16
- raise Docker::Error::ArgumentError, "Expected a Docker::Connection."
17
- end
18
- self.id = options[:id]
19
- self.connection = options[:connection]
20
- end
21
-
22
- # Create a Model with the specified body. Raises A Docker::Error::StateError
23
- # if the model already exists, and a Docker::Error::ArgumentError if the
24
- # argument is not a Hash. Otherwise, instances exec the Class's
25
- # #create_request method with the single argument.
26
- def create!(options = {}, excon_options = {})
27
- case
28
- when self.created?
29
- raise Docker::Error::StateError, "This #{self.class.name} already exists!"
30
- when !options.is_a?(Hash)
31
- raise Docker::Error::ArgumentError, 'Expected a Hash'
21
+ if !options[:connection].is_a?(Docker::Connection)
22
+ raise ArgumentError, 'Expected a Docker::Connection.'
32
23
  else
33
- instance_exec(options, excon_options, &self.class.create_request)
24
+ @id = options[:id]
25
+ @connection = options[:connection]
34
26
  end
35
27
  end
36
28
 
37
- # Returns true if the Container has been created, false otherwise.
38
- def created?
39
- !!self.id
40
- end
41
-
42
29
  def to_s
43
30
  "#{self.class.name} { :id => #{id}, :connection => #{connection} }"
44
31
  end
45
32
 
46
33
  # This defines the DSL for the including Classes.
47
34
  module ClassMethods
35
+ include Docker::Error
36
+
48
37
  # Define the Model's prefix for all requests.
49
38
  def resource_prefix(val = nil)
50
39
  val.nil? ? @resource_prefix : (@resource_prefix = val)
@@ -57,32 +46,33 @@ module Docker::Model
57
46
 
58
47
  # Define a method named `action` that sends an http `method` request to the
59
48
  # Docker Server.
60
- def docker_request(action, method, &outer_block)
49
+ def request(method, action, opts = {}, &outer_block)
61
50
  define_method(action) do |query = nil, &block|
62
- ensure_created!
63
- path = "#{self.class.resource_prefix}/#{self.id}/#{action}"
51
+ path = opts[:path]
52
+ path ||= "#{self.class.send(:resource_prefix)}/#{self.id}/#{action}"
64
53
  body = self.connection.json_request(method, path, query, &block)
65
54
  outer_block.nil? ? body : instance_exec(body, &outer_block)
66
55
  end
67
56
  end
68
57
 
58
+ [:get, :put, :post, :delete].each do |method|
59
+ define_method(method) { |*args, &block| request(method, *args, &block) }
60
+ end
61
+
62
+ # Create a Model with the specified body. Raises a
63
+ # Docker::Error::ArgumentError if the argument is not a Hash. Otherwise,
64
+ # instances execs the Class's #create_request method with the single
65
+ # argument.
66
+ def create(opts = {}, conn = Docker.connection)
67
+ raise Docker::Error::ArgumentError, 'Expected a Hash' if !opts.is_a?(Hash)
68
+ new(:connection => conn).instance_exec(opts, &create_request)
69
+ end
70
+
69
71
  # Retrieve every Instance of a model for the given server.
70
72
  def all(options = {}, connection = Docker.connection)
71
- path = "#{self.resource_prefix}/json"
73
+ path = "#{resource_prefix}/json"
72
74
  hashes = connection.json_request(:get, path, options) || []
73
75
  hashes.map { |hash| new(:id => hash['Id'], :connection => connection) }
74
76
  end
75
-
76
- private
77
- end
78
-
79
- private
80
- attr_writer :id, :connection
81
-
82
- # Raises an error unless the Model is created.
83
- def ensure_created!
84
- unless created?
85
- raise Docker::Error::StateError, "This #{self.class.name} is not created."
86
- end
87
77
  end
88
78
  end