Skip to content

Commit

Permalink
Push and Pull send progress steps to supplied block; pushing raises a…
Browse files Browse the repository at this point in the history
…n error if unauthorized.

* A block supplied to Image#push or Image.create will be called with the parsed hash of each json message streamed back from the server.
* create and push now raise errors if an error response is returned from the server
* The VCR example for creating an image was actually that of a 401 Unauthorized request. I faked data from a successful pull into the 'pushes_the_Image.yml' VCR record, and used the old version as the test case for an unauthorized call.

I'm not quite sure how I should be handling exceptions in all this. First, I'm assuming it's fair to raise an exception if any response from the server shows an error -- but if there are known-harmless errors out there they will now be explody.
Also, if an error condition occurs in the response_block, it is caught and re-thrown as an Excon error. So I'm throwing a generic error inside the block, then catching all excon errors and re-throwing only the ones I recognize (Unauthorized for push, Not Found for pull) as Docker errors.
  • Loading branch information
Philip (flip) Kromer committed Nov 29, 2014
1 parent eea96e9 commit 4dd8ad6
Show file tree
Hide file tree
Showing 6 changed files with 942 additions and 11 deletions.
50 changes: 43 additions & 7 deletions lib/docker/image.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,35 @@ def run(cmd=nil)
end

# Push the Image to the Docker registry.
def push(creds = nil, options = {})
def push(creds = nil, options = {}, &caller_blk)
repo_tag = info['RepoTags'].first
raise ArgumentError "Image is untagged" if repo_tag.nil?
repo, tag = Docker::Util.parse_repo_tag(repo_tag)
raise ArgumentError, "Image does not have a name to push." if repo.nil?

#
credentials = creds || Docker.creds || {}
headers = Docker::Util.build_auth_header(credentials)
opts = {:tag => tag}.merge(options)
connection.post("/images/#{repo}/push", opts, :headers => headers)
#
resp_blk = lambda do |chunk, *_|
steps = Docker::Util.fix_json(chunk)
steps.each do |step|
# errors thrown here are re-thrown by Excon,
# so we have to re-re-rescue our expected errors below.
raise step['error'] if step['error']
caller_blk.call(step) if caller_blk
end
end
#
connection.post("/images/#{repo}/push", opts,
:headers => headers, :response_block => resp_blk)
self
rescue StandardError => err
if err.message =~ %r{Status 401 trying to push}
raise Docker::Error::UnauthorizedError, err.message
else
raise
end
end

# Tag the Image.
Expand Down Expand Up @@ -94,13 +112,31 @@ def refresh!
class << self

# Create a new Image.
def create(opts = {}, creds = nil, conn = Docker.connection)
def create(opts = {}, creds = nil, conn = Docker.connection, &caller_blk)
credentials = creds.nil? ? Docker.creds : creds.to_json
headers = !credentials.nil? && Docker::Util.build_auth_header(credentials)
headers ||= {}
body = conn.post('/images/create', opts, :headers => headers)
id = Docker::Util.fix_json(body).select { |m| m['id'] }.last['id']
new(conn, 'id' => id, :headers => headers)
#
new_id = nil
resp_blk = lambda do |chunk, *_|
steps = Docker::Util.fix_json(chunk)
steps.each do |step|
# errors here are re-thrown by Excon, so we re-re-rescue below
raise step['error'] if step['error']
new_id = step['id'] if step['id']
caller_blk.call(step) if caller_blk
end
end
#
conn.post('/images/create', opts,
:headers => headers, :response_block => resp_blk)
new(conn, 'id' => new_id, :headers => headers)
rescue StandardError => err
if err.message =~ %r{not found in repository}
raise Docker::Error::UnauthorizedError, err.message
else
raise
end
end

# Return a specific image.
Expand Down
19 changes: 19 additions & 0 deletions spec/docker/image_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,17 @@
new_image.push(credentials)
end

it 'raises an error if unauthorized', :vcr do
expect { new_image.push(credentials.merge('username' => 'haxx0r'))
}.to raise_error(Docker::Error::UnauthorizedError)
end

it 'calls back to a supplied block', :vcr do
calls = 0
new_image.push(credentials){|step| calls += 1 if step['id'] =~ /^70/ }
expect(calls).to eq(17)
end

context 'when there are no credentials' do
let(:credentials) { nil }
let(:image) {
Expand Down Expand Up @@ -256,6 +267,14 @@
expect(image.id).to_not be_empty
expect(image.info[:headers].keys).to include 'X-Registry-Auth'
end

it 'calls back to a supplied block', :vcr do
calls = 0
subject.create('fromImage' => 'ubuntu') do |step|
calls += 1 if step['id'] =~ /^5/
end
expect(calls).to eq(22)
end
end
end

Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 4dd8ad6

Please sign in to comment.