From 3886fbda39fea2bd2d6d85af594cce3ed562fd24 Mon Sep 17 00:00:00 2001 From: nu12 <34694287+nu12@users.noreply.github.com> Date: Tue, 5 Nov 2024 09:10:53 -0500 Subject: [PATCH 1/4] Specify Docker API version --- .github/workflows/cd.yml | 7 ++++--- .github/workflows/ci.yml | 4 ++-- Gemfile.lock | 31 ++++++++++++++++--------------- lib/docker/api/base.rb | 1 + lib/docker/api/system.rb | 6 +++--- spec/endpoints/container_spec.rb | 12 ++++++------ spec/endpoints/image_spec.rb | 30 +++++++++++++++--------------- spec/endpoints/system_spec.rb | 12 ++++++------ 8 files changed, 53 insertions(+), 50 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index a138d55..5a84f3b 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -1,7 +1,8 @@ name: CD on: push: - branches: [ main ] + tags: + - 'v*.*.*' jobs: release: @@ -12,9 +13,9 @@ jobs: steps: - uses: actions/checkout@v3 - name: Set up Ruby - uses: ruby/setup-ruby@21351ecc0a7c196081abca5dc55b08f085efe09a + uses: ruby/setup-ruby@v1 with: - ruby-version: 2.6 + ruby-version: 3.3 - name: Setup to RubyGems run: | mkdir -p $HOME/.gem diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 84d25e7..87f108d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,9 +10,9 @@ jobs: steps: - uses: actions/checkout@v2 - name: Set up Ruby - uses: ruby/setup-ruby@21351ecc0a7c196081abca5dc55b08f085efe09a + uses: ruby/setup-ruby@v1 with: - ruby-version: 2.6 + ruby-version: 3.3 - name: Install Docker run: curl https://get.docker.com | sh - name: Install dependencies diff --git a/Gemfile.lock b/Gemfile.lock index b155bbe..eecaf16 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,30 +1,31 @@ PATH remote: . specs: - dockerapi (0.19.0) + dockerapi (0.20.0) excon (~> 0.79) GEM remote: https://rubygems.org/ specs: - diff-lcs (1.3) - excon (0.79.0) + diff-lcs (1.5.1) + excon (0.112.0) rake (12.3.3) - rspec (3.9.0) - rspec-core (~> 3.9.0) - rspec-expectations (~> 3.9.0) - rspec-mocks (~> 3.9.0) - rspec-core (3.9.2) - rspec-support (~> 3.9.3) - rspec-expectations (3.9.2) + rspec (3.13.0) + rspec-core (~> 3.13.0) + rspec-expectations (~> 3.13.0) + rspec-mocks (~> 3.13.0) + rspec-core (3.13.2) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.3) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.9.0) - rspec-mocks (3.9.1) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.2) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.9.0) - rspec-support (3.9.3) + rspec-support (~> 3.13.0) + rspec-support (3.13.1) PLATFORMS + arm64-darwin-23 ruby DEPENDENCIES @@ -33,4 +34,4 @@ DEPENDENCIES rspec (~> 3.0) BUNDLED WITH - 2.1.4 + 2.5.11 diff --git a/lib/docker/api/base.rb b/lib/docker/api/base.rb index ae79fe3..49f0c9b 100644 --- a/lib/docker/api/base.rb +++ b/lib/docker/api/base.rb @@ -89,6 +89,7 @@ def hash_to_params hash # @param path [String]: Base URL string. # @param hash [Hash]: Hash object to be appended to the URL as query parameters. def build_path path, params = {} + path = "/v#{Docker::API::API_VERSION}#{path}" params.size > 0 ? [path, hash_to_params(params)].join("?") : path end diff --git a/lib/docker/api/system.rb b/lib/docker/api/system.rb index 344ee8f..73a9d37 100644 --- a/lib/docker/api/system.rb +++ b/lib/docker/api/system.rb @@ -32,7 +32,7 @@ def events params = {}, &block # Docker API: GET /_ping # @see https://docs.docker.com/engine/api/v1.40/#operation/SystemPing def ping - @connection.get("/_ping") + @connection.get(build_path("/_ping")) end ## @@ -41,7 +41,7 @@ def ping # Docker API: GET /info # @see https://docs.docker.com/engine/api/v1.40/#operation/SystemInfo def info - @connection.get("/info") + @connection.get(build_path("/info")) end ## @@ -50,7 +50,7 @@ def info # Docker API: GET /version # @see https://docs.docker.com/engine/api/v1.40/#operation/SystemVersion def version - @connection.get("/version") + @connection.get(build_path("/version")) end ## diff --git a/spec/endpoints/container_spec.rb b/spec/endpoints/container_spec.rb index 694087f..8e69422 100644 --- a/spec/endpoints/container_spec.rb +++ b/spec/endpoints/container_spec.rb @@ -20,9 +20,9 @@ end describe "request path" do - it { expect(subject.list( { all: true, filters: {name: {"#{name}": true}} } ).path).to eq("/containers/json?all=true&filters={\"name\":{\"#{name}\":true}}") } - it { expect(subject.list( { all: true, filters: {exited: {"0": true} } } ).path).to eq("/containers/json?all=true&filters={\"exited\":{\"0\":true}}") } - it { expect(subject.list( { all: true, filters: {status: ["running"] } } ).path).to eq("/containers/json?all=true&filters={\"status\":[\"running\"]}") } + it { expect(subject.list( { all: true, filters: {name: {"#{name}": true}} } ).path).to eq("/v#{Docker::API::API_VERSION}/containers/json?all=true&filters={\"name\":{\"#{name}\":true}}") } + it { expect(subject.list( { all: true, filters: {exited: {"0": true} } } ).path).to eq("/v#{Docker::API::API_VERSION}/containers/json?all=true&filters={\"exited\":{\"0\":true}}") } + it { expect(subject.list( { all: true, filters: {status: ["running"] } } ).path).to eq("/v#{Docker::API::API_VERSION}/containers/json?all=true&filters={\"status\":[\"running\"]}") } end end @@ -98,7 +98,7 @@ it { expect(subject.start(name).status).to eq(204 )} it { expect(subject.start("doesn-exist").status).to eq(404 )} it { expect(subject.start(name, {detachKeys: "ctrl-c"}).status).to eq(204 )} - it { expect(subject.start(name, {detachKeys: "ctrl-c"}).path).to eq("/containers/#{name}/start?detachKeys=ctrl-c" )} + it { expect(subject.start(name, {detachKeys: "ctrl-c"}).path).to eq("/v#{Docker::API::API_VERSION}/containers/#{name}/start?detachKeys=ctrl-c" )} it { expect{subject.start(name, {invalid_value: "invalid"})}.to raise_error(Docker::API::InvalidParameter )} it do subject.start(name) @@ -126,7 +126,7 @@ it { expect(subject.kill(name).status).to be(204) } it { expect(subject.kill("doesn-exist").status).to be(404) } it { expect(subject.kill(name, {signal: "SIGKILL"}).status).to eq(204) } - it { expect(subject.kill(name, {signal: "SIGKILL"}).path).to eq("/containers/#{name}/kill?signal=SIGKILL") } + it { expect(subject.kill(name, {signal: "SIGKILL"}).path).to eq("/v#{Docker::API::API_VERSION}/containers/#{name}/kill?signal=SIGKILL") } it { expect{subject.kill(name, {invalid_value: "invalid"})}.to raise_error(Docker::API::InvalidParameter) } it do subject.stop(name) @@ -139,7 +139,7 @@ it { expect(subject.restart("doesn-exist").status).to be(404) } it { expect(subject.restart(name, {t: 2}).status).to eq(204) } it { expect(subject.restart(name, {signal: "SIGINT"}).status).to eq(204) } - it { expect(subject.restart(name, {t: 2}).path).to eq("/containers/#{name}/restart?t=2") } + it { expect(subject.restart(name, {t: 2}).path).to eq("/v#{Docker::API::API_VERSION}/containers/#{name}/restart?t=2") } it { expect{subject.restart(name, {invalid_value: "invalid"})}.to raise_error(Docker::API::InvalidParameter) } end diff --git a/spec/endpoints/image_spec.rb b/spec/endpoints/image_spec.rb index dfab483..2e8afea 100644 --- a/spec/endpoints/image_spec.rb +++ b/spec/endpoints/image_spec.rb @@ -41,16 +41,16 @@ it { expect(subject.list(all: true, filters: {since: {"#{image}": true}}).status).to eq(200) } end describe "request path" do - it { expect(subject.list(all: true).path).to eq("/images/json?all=true") } - it { expect(subject.list(all: true, "shared-size": true).path).to eq("/images/json?all=true&shared-size=true") } - it { expect(subject.list(digests: true).path).to eq("/images/json?digests=true") } - it { expect(subject.list(all: true, digests: true).path).to eq("/images/json?all=true&digests=true") } - it { expect(subject.list(all: true, filters: {dangling: {"true": true}}).path).to eq("/images/json?all=true&filters={\"dangling\":{\"true\":true}}") } - it { expect(subject.list(all: true, filters: {label: {"label-here": true}}).path).to eq("/images/json?all=true&filters={\"label\":{\"label-here\":true}}") } - it { expect(subject.list(all: true, filters: {reference: {"#{image}": true}}).path).to eq("/images/json?all=true&filters={\"reference\":{\"#{image}\":true}}") } - it { expect(subject.list(all: true, filters: {before: {"#{image}": true}}).path).to eq("/images/json?all=true&filters={\"before\":{\"#{image}\":true}}") } - it { expect(subject.list(all: true, filters: {since: {"#{image}": true}}).path).to eq("/images/json?all=true&filters={\"since\":{\"#{image}\":true}}") } - it { expect(subject.list(all: true, invalid: true, skip_validation: true).path).to eq("/images/json?all=true&invalid=true") } + it { expect(subject.list(all: true).path).to eq("/v#{Docker::API::API_VERSION}/images/json?all=true") } + it { expect(subject.list(all: true, "shared-size": true).path).to eq("/v#{Docker::API::API_VERSION}/images/json?all=true&shared-size=true") } + it { expect(subject.list(digests: true).path).to eq("/v#{Docker::API::API_VERSION}/images/json?digests=true") } + it { expect(subject.list(all: true, digests: true).path).to eq("/v#{Docker::API::API_VERSION}/images/json?all=true&digests=true") } + it { expect(subject.list(all: true, filters: {dangling: {"true": true}}).path).to eq("/v#{Docker::API::API_VERSION}/images/json?all=true&filters={\"dangling\":{\"true\":true}}") } + it { expect(subject.list(all: true, filters: {label: {"label-here": true}}).path).to eq("/v#{Docker::API::API_VERSION}/images/json?all=true&filters={\"label\":{\"label-here\":true}}") } + it { expect(subject.list(all: true, filters: {reference: {"#{image}": true}}).path).to eq("/v#{Docker::API::API_VERSION}/images/json?all=true&filters={\"reference\":{\"#{image}\":true}}") } + it { expect(subject.list(all: true, filters: {before: {"#{image}": true}}).path).to eq("/v#{Docker::API::API_VERSION}/images/json?all=true&filters={\"before\":{\"#{image}\":true}}") } + it { expect(subject.list(all: true, filters: {since: {"#{image}": true}}).path).to eq("/v#{Docker::API::API_VERSION}/images/json?all=true&filters={\"since\":{\"#{image}\":true}}") } + it { expect(subject.list(all: true, invalid: true, skip_validation: true).path).to eq("/v#{Docker::API::API_VERSION}/images/json?all=true&invalid=true") } end it { expect{subject.list(invalid: "invalid")}.to raise_error(Docker::API::InvalidParameter) } end @@ -159,11 +159,11 @@ it { expect(subject.prune(filters: {label: {"LABEL": true}, dangling: {"1": true}}).status).to eq(200) } end describe "request path" do - it { expect(subject.prune(filters: {dangling: {"true": true}}).path).to eq("/images/prune?filters={\"dangling\":{\"true\":true}}") } - it { expect(subject.prune(filters: {dangling: {"1": true}}).path).to eq("/images/prune?filters={\"dangling\":{\"1\":true}}") } - it { expect(subject.prune(filters: {until: {"10m": true}}).path).to eq("/images/prune?filters={\"until\":{\"10m\":true}}") } - it { expect(subject.prune(filters: {label: {"LABEL": true}}).path).to eq("/images/prune?filters={\"label\":{\"LABEL\":true}}") } - it { expect(subject.prune(filters: {label: {"LABEL": true}, dangling: {"1": true}}).path).to eq("/images/prune?filters={\"label\":{\"LABEL\":true},\"dangling\":{\"1\":true}}") } + it { expect(subject.prune(filters: {dangling: {"true": true}}).path).to eq("/v#{Docker::API::API_VERSION}/images/prune?filters={\"dangling\":{\"true\":true}}") } + it { expect(subject.prune(filters: {dangling: {"1": true}}).path).to eq("/v#{Docker::API::API_VERSION}/images/prune?filters={\"dangling\":{\"1\":true}}") } + it { expect(subject.prune(filters: {until: {"10m": true}}).path).to eq("/v#{Docker::API::API_VERSION}/images/prune?filters={\"until\":{\"10m\":true}}") } + it { expect(subject.prune(filters: {label: {"LABEL": true}}).path).to eq("/v#{Docker::API::API_VERSION}/images/prune?filters={\"label\":{\"LABEL\":true}}") } + it { expect(subject.prune(filters: {label: {"LABEL": true}, dangling: {"1": true}}).path).to eq("/v#{Docker::API::API_VERSION}/images/prune?filters={\"label\":{\"LABEL\":true},\"dangling\":{\"1\":true}}") } end it { expect{subject.prune( invalid: "invalid")}.to raise_error(Docker::API::InvalidParameter) } end diff --git a/spec/endpoints/system_spec.rb b/spec/endpoints/system_spec.rb index 302bd80..4e57df5 100644 --- a/spec/endpoints/system_spec.rb +++ b/spec/endpoints/system_spec.rb @@ -10,7 +10,7 @@ describe ".ping" do it { expect(subject).to respond_to(:ping) } it { expect(subject.ping.status).to eq(200) } - it { expect(subject.ping.path).to eq("/_ping") } + it { expect(subject.ping.path).to eq("/v#{Docker::API::API_VERSION}/_ping") } end describe ".info" do @@ -18,7 +18,7 @@ it { expect(subject.info.status).to eq(200) } it { expect(subject.info.success?).to eq(true) } it { expect(subject.info.json).to be_kind_of(Hash) } - it { expect(subject.info.path).to eq("/info") } + it { expect(subject.info.path).to eq("/v#{Docker::API::API_VERSION}/info") } end describe ".version" do @@ -26,7 +26,7 @@ it { expect(subject.version.status).to eq(200) } it { expect(subject.version.success?).to eq(true) } it { expect(subject.version.json).to be_kind_of(Hash) } - it { expect(subject.version.path).to eq("/version") } + it { expect(subject.version.path).to eq("/v#{Docker::API::API_VERSION}/version") } end describe ".events" do @@ -35,7 +35,7 @@ it { expect(described_class.new).to respond_to(:events) } it { expect(subject.status).to eq(200) } it { expect(subject.success?).to eq(true) } - it { expect(subject.path).to eq("/events?until=#{now}") } + it { expect(subject.path).to eq("/v#{Docker::API::API_VERSION}/events?until=#{now}") } it { expect{described_class.new.events(invalid: true)}.to raise_error(Docker::API::InvalidParameter) } it { expect{described_class.new.events(invalid: true, skip_validation: false)}.to raise_error(Docker::API::InvalidParameter) } end @@ -45,9 +45,9 @@ it { expect(subject.df.status).to eq(200) } it { expect(subject.df.success?).to eq(true) } it { expect(subject.df.json).to be_kind_of(Hash) } - it { expect(subject.df.path).to eq("/system/df") } + it { expect(subject.df.path).to eq("/v#{Docker::API::API_VERSION}/system/df") } it { expect{subject.df(invalid: "true")}.to raise_error(Docker::API::InvalidParameter) } it { expect{subject.df(type: "container")}.not_to raise_error } - it { expect(subject.df(type: "container").path).to eq("/system/df?type=container" )} + it { expect(subject.df(type: "container").path).to eq("/v#{Docker::API::API_VERSION}/system/df?type=container" )} end end \ No newline at end of file From 225f50fb89fdebd6583991125ad228dfdb6efa55 Mon Sep 17 00:00:00 2001 From: nu12 <34694287+nu12@users.noreply.github.com> Date: Tue, 5 Nov 2024 09:19:39 -0500 Subject: [PATCH 2/4] Fix Github link --- spec/endpoints/image_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/endpoints/image_spec.rb b/spec/endpoints/image_spec.rb index 2e8afea..d7fab87 100644 --- a/spec/endpoints/image_spec.rb +++ b/spec/endpoints/image_spec.rb @@ -174,10 +174,10 @@ it { expect(subject.build("resources/build.tar.xz", q: true, rm: false).status).to eq(200) } it { expect(subject.build("resources/build.tar.xz", memory: 4000000, rm: true, forcerm:true).status).to eq(200) } it { expect(subject.build("resources/build.tar.xz", memory: 4000000, rm: true, forcerm:true, pull:true).status).to eq(200) } - it { expect(subject.build(nil, remote: "https://github.com/nu12/dockerapi/blob/master/resources/build.tar.xz?raw=true").status).to eq(200) } + it { expect(subject.build(nil, remote: "https://github.com/nu12/dockerapi/raw/refs/heads/main/resources/build.tar.xz").status).to eq(200) } it { expect(subject.build(nil, remote: "https://raw.githubusercontent.com/nu12/dockerapi/master/resources/Dockerfile").status).to eq(200) } it { expect{subject.build("resources/build.tar.xz", invalid: "invalid")}.to raise_error(Docker::API::InvalidParameter) } - it { expect{subject.build(nil, remote: "https://github.com/nu12/dockerapi/blob/master/resources/build.tar.xz?raw=true", invalid: "invalid")}.to raise_error(Docker::API::InvalidParameter) } + it { expect{subject.build(nil, remote: "https://github.com/nu12/dockerapi/raw/refs/heads/main/resources/build.tar.xz", invalid: "invalid")}.to raise_error(Docker::API::InvalidParameter) } it { expect{subject.build(nil, invalid: "invalid", skip_validation: true)}.to raise_error(Docker::API::Error) } end From dcd7c8554caaf0a74c3b535ef392245100ad876e Mon Sep 17 00:00:00 2001 From: nu12 <34694287+nu12@users.noreply.github.com> Date: Tue, 5 Nov 2024 09:33:13 -0500 Subject: [PATCH 3/4] Fix network specs --- lib/docker/api/network.rb | 8 ++++---- spec/endpoints/network_spec.rb | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/docker/api/network.rb b/lib/docker/api/network.rb index d94ddd9..ec96b57 100644 --- a/lib/docker/api/network.rb +++ b/lib/docker/api/network.rb @@ -34,7 +34,7 @@ def details name, params = {} # # @param body [Hash]: Request body to be sent as json. def create body = {} - @connection.request(method: :post, path: "/networks/create", headers: {"Content-Type": "application/json"}, body: body.to_json) + @connection.request(method: :post, path: build_path("/networks/create"), headers: {"Content-Type": "application/json"}, body: body.to_json) end ## @@ -45,7 +45,7 @@ def create body = {} # # @param name [String]: The ID or name of the network. def remove name - @connection.delete("/networks/#{name}") + @connection.delete(build_path("/networks/#{name}")) end ## @@ -68,7 +68,7 @@ def prune params = {} # @param name [String]: The ID or name of the network. # @param body [Hash]: Request body to be sent as json. def connect name, body = {} - @connection.request(method: :post, path: "/networks/#{name}/connect", headers: {"Content-Type": "application/json"}, body: body.to_json) + @connection.request(method: :post, path: build_path("/networks/#{name}/connect"), headers: {"Content-Type": "application/json"}, body: body.to_json) end ## @@ -80,7 +80,7 @@ def connect name, body = {} # @param name [String]: The ID or name of the network. # @param body [Hash]: Request body to be sent as json. def disconnect name, body = {} - @connection.request(method: :post, path: "/networks/#{name}/disconnect", headers: {"Content-Type": "application/json"}, body: body.to_json) + @connection.request(method: :post, path: build_path("/networks/#{name}/disconnect"), headers: {"Content-Type": "application/json"}, body: body.to_json) end end \ No newline at end of file diff --git a/spec/endpoints/network_spec.rb b/spec/endpoints/network_spec.rb index 6d11a85..4e06fd0 100644 --- a/spec/endpoints/network_spec.rb +++ b/spec/endpoints/network_spec.rb @@ -21,7 +21,7 @@ end describe ".create" do - it { expect(subject.create.status).to eq(500) } + it { expect(subject.create.status).to eq(400) } it do expect(subject.create( Name: "rspec-network", From b1beed82d28465e65e2e9b299d40b2db4fbbe437 Mon Sep 17 00:00:00 2001 From: nu12 <34694287+nu12@users.noreply.github.com> Date: Tue, 5 Nov 2024 10:12:56 -0500 Subject: [PATCH 4/4] Fix ip_address & path typo --- lib/docker/api/node.rb | 2 +- spec/endpoints/node_spec.rb | 2 +- spec/endpoints/swarm_spec.rb | 4 ++-- spec/spec_helper.rb | 8 ++++++++ 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/docker/api/node.rb b/lib/docker/api/node.rb index 129f65c..6b2eb43 100644 --- a/lib/docker/api/node.rb +++ b/lib/docker/api/node.rb @@ -25,7 +25,7 @@ def list params = {} # @param params [Hash]: Parameters that are appended to the URL. # @param body [Hash]: Request body to be sent as json. def update name, params = {}, body = {} - @connection.request(method: :post, path: build_path("nodes/#{name}/update", params), headers: {"Content-Type": "application/json"}, body: body.to_json) + @connection.request(method: :post, path: build_path("/nodes/#{name}/update", params), headers: {"Content-Type": "application/json"}, body: body.to_json) end ## diff --git a/spec/endpoints/node_spec.rb b/spec/endpoints/node_spec.rb index a824b83..effbbc5 100644 --- a/spec/endpoints/node_spec.rb +++ b/spec/endpoints/node_spec.rb @@ -1,5 +1,5 @@ RSpec.describe Docker::API::Node do - ip_address = Socket.ip_address_list[2].ip_address + ip_address = get_api_ip_address subject { described_class.new } it { is_expected.to respond_to(:list) } it { is_expected.to respond_to(:details) } diff --git a/spec/endpoints/swarm_spec.rb b/spec/endpoints/swarm_spec.rb index 709ec56..e739384 100644 --- a/spec/endpoints/swarm_spec.rb +++ b/spec/endpoints/swarm_spec.rb @@ -1,6 +1,6 @@ -require 'socket' + RSpec.describe Docker::API::Swarm do - ip_address = Socket.ip_address_list[2].ip_address + ip_address = get_api_ip_address subject { described_class.new } describe ".init" do diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index e3cda6d..6049973 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -16,3 +16,11 @@ c.syntax = :expect end end + +def get_api_ip_address + + Socket.ip_address_list.each do |addr| + return addr.ip_address if addr.ipv4? && !addr.ipv4_loopback? && addr.ip_address =~ /\A\d{1,3}(\.\d{1,3}){3}\z/ + end + +end \ No newline at end of file