Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .cane
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
--abc-max 19
--style-measure 100
--abc-max 30
--style-measure 120
66 changes: 66 additions & 0 deletions .github/workflows/unit_test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
name: Unit Tests
on:
push:
branches:
# A test branch for seeing if your tests will pass in your personal fork
- test_me_github
pull_request:
branches:
- main
- master
jobs:
docker-rspec:
runs-on:
- ubuntu-latest
strategy:
matrix:
ruby:
- 2.7
- 2.6
- 2.5
- 2.4
docker_version:
- 5:19.03.8~3-0~ubuntu-bionic
- 5:18.09.9~3-0~ubuntu-bionic
- 18.06.3~ce~3-0~ubuntu
fail-fast: true
steps:
- uses: actions/checkout@v2
- uses: actions/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby }}
- name: install bundler
run: |
gem install bundler -v '~> 1.17.3'
bundle update
- name: install docker
env:
DOCKER_VERSION: ${{ matrix.docker_version }}
run: sudo ./script/install_docker.sh ${DOCKER_VERSION} ${DOCKER_CE}
- name: spec tests
run: bundle exec rake

podman-rspec:
runs-on:
- ubuntu-latest
strategy:
matrix:
ruby:
- 2.7
- 2.6
- 2.5
- 2.4
fail-fast: true
steps:
- uses: actions/checkout@v2
- uses: actions/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby }}
- name: install bundler
run: |
gem install bundler -v '~> 1.17.3'
bundle update
- name: install podman
run: sudo ./script/install_podman.sh
- name: spec tests
run: bundle exec rake
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ Usage

docker-api is designed to be very lightweight. Almost no state is cached (aside from id's which are immutable) to ensure that each method call's information is up to date. As such, just about every external method represents an API call.

At this time, basic `podman` support has been added via the podman docker-compatible API socket.

## Starting up

Follow the [installation instructions](https://docs.docker.com/install/), and then run:
Expand Down
2 changes: 1 addition & 1 deletion Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ end

desc 'Download the necessary base images'
task :unpack do
%w( swipely/base registry busybox tianon/true debian:wheezy ).each do |image|
%w( swipely/base registry busybox tianon/true debian:stable ).each do |image|
system "docker pull #{image}"
end
end
Expand Down
25 changes: 24 additions & 1 deletion lib/docker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,29 @@ def ping(connection = self.connection)
connection.get('/_ping')
end

# Determine if the server is podman or docker.
def podman?(connection = self.connection)
@attrs ||= {}
return @attrs[:podman] if @attrs[:podman]

@attrs[:podman] = !(
Array(version(connection)['Components']).find do |component|
component['Name'] && component['Name'].include?('Podman')
end
).nil?

@attrs[:podman]
end

# Determine if the session is rootless.
def rootless?(connection = self.connection)
@attrs ||= {}
return @attrs[:rootless] if @attrs[:rootless]

@attrs[:rootless] = (info(connection)['Rootless'] == true)
@attrs[:rootless]
end

# Login to the Docker registry.
def authenticate!(options = {}, connection = self.connection)
creds = MultiJson.dump(options)
Expand All @@ -132,5 +155,5 @@ def authenticate!(options = {}, connection = self.connection)
module_function :default_socket_url, :env_url, :url, :url=, :env_options,
:options, :options=, :creds, :creds=, :logger, :logger=,
:connection, :reset!, :reset_connection!, :version, :info,
:ping, :authenticate!, :ssl_options
:ping, :podman?, :rootless?, :authenticate!, :ssl_options
end
63 changes: 50 additions & 13 deletions lib/docker/connection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,58 @@ def resource

# Send a request to the server with the `
def request(*args, &block)
retries ||= 0
request = compile_request_params(*args, &block)
log_request(request)
resource.request(request).body
rescue Excon::Errors::BadRequest => ex
raise ClientError, ex.response.body
rescue Excon::Errors::Unauthorized => ex
raise UnauthorizedError, ex.response.body
rescue Excon::Errors::NotFound => ex
raise NotFoundError, ex.response.body
rescue Excon::Errors::Conflict => ex
raise ConflictError, ex.response.body
rescue Excon::Errors::InternalServerError => ex
raise ServerError, ex.response.body
rescue Excon::Errors::Timeout => ex
raise TimeoutError, ex.message
begin
resource.request(request).body
rescue Excon::Errors::BadRequest => ex
if retries < 2
response_cause = ''
begin
response_cause = JSON.parse(ex.response.body)['cause']
rescue JSON::ParserError
#noop
end

if response_cause.is_a?(String)
# The error message will tell the application type given and then the
# application type that the message should be
#
# This is not perfect since it relies on processing a message that
# could change in the future. However, it should be a good stop-gap
# until all methods are updated to pass in the appropriate content
# type.
#
# A current example message is:
# * 'Content-Type: application/json is not supported. Should be "application/x-tar"'
matches = response_cause.delete('"\'').scan(%r{(application/\S+)})
unless matches.count < 2
Docker.logger.warn(
<<~RETRY_WARNING
Automatically retrying with content type '#{response_cause}'
Original Error: #{ex}
RETRY_WARNING
) if Docker.logger

request[:headers]['Content-Type'] = matches.last.first
retries += 1
retry
end
end
end
raise ClientError, ex.response.body
rescue Excon::Errors::Unauthorized => ex
raise UnauthorizedError, ex.response.body
rescue Excon::Errors::NotFound => ex
raise NotFoundError, ex.response.body
rescue Excon::Errors::Conflict => ex
raise ConflictError, ex.response.body
rescue Excon::Errors::InternalServerError => ex
raise ServerError, ex.response.body
rescue Excon::Errors::Timeout => ex
raise TimeoutError, ex.message
end
end

def log_request(request)
Expand Down
7 changes: 7 additions & 0 deletions lib/docker/exec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ def to_s
# @return [Docker::Exec] self
def self.create(options = {}, conn = Docker.connection)
container = options.delete('Container')

# Podman does not attach these by default but does require them to be attached
if ::Docker.podman?
options['AttachStderr'] = true if options['AttachStderr'].nil?
options['AttachStdout'] = true if options['AttachStdout'].nil?
end

resp = conn.post("/containers/#{container}/exec", {},
body: MultiJson.dump(options))
hash = Docker::Util.parse_json(resp) || {}
Expand Down
22 changes: 20 additions & 2 deletions lib/docker/image.rb
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,16 @@ def insert_local(opts = {})

# Remove the Image from the server.
def remove(opts = {})
name = opts.delete(:name) || self.id
name = opts.delete(:name)

unless name
if ::Docker.podman?
name = self.id.split(':').last
else
name = self.id
end
end

connection.delete("/images/#{name}", opts)
end
alias_method :delete, :remove
Expand Down Expand Up @@ -227,7 +236,16 @@ def search(query = {}, connection = Docker.connection, creds = nil)
# Import an Image from the output of Docker::Container#export. The first
# argument may either be a File or URI.
def import(imp, opts = {}, conn = Docker.connection)
open(imp) do |io|
require 'open-uri'

# This differs after Ruby 2.4
if URI.public_methods.include?(:open)
munged_open = URI.method(:open)
else
munged_open = self.method(:open)
end

munged_open.call(imp) do |io|
import_stream(opts, conn) do
io.read(Excon.defaults[:chunk_size]).to_s
end
Expand Down
12 changes: 12 additions & 0 deletions script/install_podman.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/bin/sh
set -ex

. /etc/os-release

curl -L https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/xUbuntu_${VERSION_ID}/Release.key | sudo apt-key add -

echo "deb https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/xUbuntu_18.04/ /" > /etc/apt/sources.list.d/podman.list

apt-get update

apt-get install -y podman
6 changes: 3 additions & 3 deletions spec/docker/connection_spec.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
require 'spec_helper'

SingleCov.covered! uncovered: 7
SingleCov.covered! uncovered: 11

describe Docker::Connection do
subject { described_class.new('http://localhost:4243', {}) }
Expand All @@ -20,11 +20,11 @@

context 'when the first argument is a String' do
context 'and the url is a unix socket' do
let(:url) { 'unix:///var/run/docker.sock' }
let(:url) { ::Docker.env_url || ::Docker.default_socket_url }

it 'sets the socket path in the options' do
expect(subject.url).to eq('unix:///')
expect(subject.options).to include(:socket => '/var/run/docker.sock')
expect(subject.options).to include(:socket => url.split('//').last)
end
end

Expand Down
Loading