c59deca51e
Raw OpenSSL sockets don't find the system CA bundle the same way Net::HTTP does. ssl_verify (default: true) is now a config setting that applies to both HTTP REST calls and WebSocket connections. Set via `portainer-cli configure`. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
107 lines
2.4 KiB
Ruby
107 lines
2.4 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require 'net/http'
|
|
require 'json'
|
|
require 'uri'
|
|
require 'openssl'
|
|
|
|
module PortainerCli
|
|
class Client
|
|
class ApiError < StandardError
|
|
attr_reader :code
|
|
def initialize(msg, code)
|
|
super(msg)
|
|
@code = code
|
|
end
|
|
end
|
|
|
|
def initialize(config)
|
|
@config = config
|
|
@base = URI.parse(config.url.chomp('/'))
|
|
end
|
|
|
|
def get(path, params = {})
|
|
request(:get, path, params: params)
|
|
end
|
|
|
|
def post(path, body = nil)
|
|
request(:post, path, body: body)
|
|
end
|
|
|
|
def delete(path)
|
|
request(:delete, path)
|
|
end
|
|
|
|
# Returns the raw websocket URL (ws:// or wss://) for exec sessions
|
|
def websocket_url(path, params = {})
|
|
uri = ws_uri(path)
|
|
uri.query = URI.encode_www_form(params) unless params.empty?
|
|
uri.to_s
|
|
end
|
|
|
|
def auth_header
|
|
@config.auth_header
|
|
end
|
|
|
|
private
|
|
|
|
def request(method, path, params: {}, body: nil)
|
|
uri = http_uri(path)
|
|
uri.query = URI.encode_www_form(params) unless params.empty?
|
|
|
|
http = build_http(uri)
|
|
req = build_request(method, uri, body)
|
|
|
|
resp = http.request(req)
|
|
handle_response(resp)
|
|
end
|
|
|
|
def build_http(uri)
|
|
http = Net::HTTP.new(uri.host, uri.port)
|
|
if uri.scheme == 'https'
|
|
http.use_ssl = true
|
|
http.verify_mode = @config.ssl_verify ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE
|
|
end
|
|
http.open_timeout = 10
|
|
http.read_timeout = 30
|
|
http
|
|
end
|
|
|
|
def build_request(method, uri, body)
|
|
klass = {
|
|
get: Net::HTTP::Get,
|
|
post: Net::HTTP::Post,
|
|
delete: Net::HTTP::Delete
|
|
}.fetch(method)
|
|
|
|
req = klass.new(uri)
|
|
req['Content-Type'] = 'application/json'
|
|
req['Accept'] = 'application/json'
|
|
@config.auth_header.each { |k, v| req[k] = v }
|
|
|
|
req.body = JSON.generate(body) if body
|
|
req
|
|
end
|
|
|
|
def handle_response(resp)
|
|
code = resp.code.to_i
|
|
raise ApiError.new("HTTP #{code}: #{resp.body}", code) if code >= 400
|
|
|
|
return nil if resp.body.nil? || resp.body.empty?
|
|
|
|
JSON.parse(resp.body)
|
|
rescue JSON::ParserError
|
|
resp.body
|
|
end
|
|
|
|
def http_uri(path)
|
|
URI.parse("#{@base}#{path}")
|
|
end
|
|
|
|
def ws_uri(path)
|
|
scheme = @base.scheme == 'https' ? 'wss' : 'ws'
|
|
URI.parse("#{scheme}://#{@base.host}:#{@base.port}#{path}")
|
|
end
|
|
end
|
|
end
|