# 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 = OpenSSL::SSL::VERIFY_PEER 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