class Dapp::Kube::Kubernetes::Client

Attributes

config[R]
context[R]
namespace[R]
timeout[R]

Public Class Methods

new(config, context, namespace, timeout: nil) click to toggle source
# File lib/dapp/kube/kubernetes/client.rb, line 85
def initialize(config, context, namespace, timeout: nil)
  @config = config
  @context = context
  @namespace = namespace
  @timeout = timeout
  @query_parameters = {}
  @cluster_version
end

Public Instance Methods

cluster_version(**query_parameters) click to toggle source

minikube returns empty major and minor. Fallback to stable only apis for minikube setup

# File lib/dapp/kube/kubernetes/client.rb, line 192
def cluster_version(**query_parameters)
  version_obj = request!(:get, "/version", **query_parameters)
  @cluster_version ||= begin
    major = version_obj['major']
    minor = version_obj['minor'].gsub(/\+$/,'')
    k8s_version = "#{major}.#{minor}"
    if K8S_API_ENDPOINTS.has_key?(k8s_version)
      k8s_version
    else
      "stable"
    end
  end
end
create_namespace!(name, **query_parameters) click to toggle source
# File lib/dapp/kube/kubernetes/client.rb, line 183
def create_namespace!(name, **query_parameters)
  request!(:post, '/api/v1/namespaces', body: { metadata: { name: name } }, **query_parameters)
end
delete_namespace!(name, **query_parameters) click to toggle source
# File lib/dapp/kube/kubernetes/client.rb, line 187
def delete_namespace!(name, **query_parameters)
  request!(:delete, "/api/v1/namespaces/#{name}", **query_parameters)
end
event_list(**query_parameters) click to toggle source
# File lib/dapp/kube/kubernetes/client.rb, line 228
def event_list(**query_parameters)
  request!(:get, "/api/v1/namespaces/#{namespace}/events", **query_parameters)
end
namespace?(name, **query_parameters) click to toggle source
# File lib/dapp/kube/kubernetes/client.rb, line 179
def namespace?(name, **query_parameters)
  namespace_list(**query_parameters)['items'].map { |item| item['metadata']['name'] }.include?(name)
end
namespace_list(**query_parameters) click to toggle source
# File lib/dapp/kube/kubernetes/client.rb, line 175
def namespace_list(**query_parameters)
  request!(:get, '/api/v1/namespaces', **query_parameters)
end
pod_log(name, follow: false, **query_parameters, &blk) click to toggle source
# File lib/dapp/kube/kubernetes/client.rb, line 206
def pod_log(name, follow: false, **query_parameters, &blk)
  excon_parameters = follow ? { response_block: blk } : {}
  request!(:get,
           "/api/v1/namespaces/#{namespace}/pods/#{name}/log",
           excon_parameters: excon_parameters,
           response_body_parameters: {json: false},
           **{ follow: follow }.merge(query_parameters))
rescue Excon::Error::Timeout
  raise Error::Timeout
rescue Error::Base => err
  if err.net_status[:code] == :bad_request and err.net_status[:data][:response_body]
    msg = err.net_status[:data][:response_body]['message']
    if msg.end_with? 'ContainerCreating'
      raise Error::Pod::ContainerCreating, data: err.net_status[:data]
    elsif msg.end_with? 'PodInitializing'
      raise Error::Pod::PodInitializing, data: err.net_status[:data]
    end
  end

  raise
end
resource_endpoint_path(resource) click to toggle source

NOTICE: Название метода аналогично kind'у выдаваемого результата. NOTICE: В данном случае в результате kind=DeploymentList. NOTICE: Методы создания/обновления/удаления сущностей kubernetes заканчиваются на '!'. Например, create_deployment!. В каждом методе происходит выбор api на основе версии кластера

# File lib/dapp/kube/kubernetes/client.rb, line 122
def resource_endpoint_path(resource)
  K8S_API_ENDPOINTS[cluster_version()].map do |path, resources|
    resources.include?(resource) ? path : nil
  end.compact.first
end
with_namespace(namespace) { || ... } click to toggle source

Чтобы не перегружать методы явной передачей namespace. Данный метод может пригодиться только в ситуации, когда надо указать другой namespace, в большинстве случаев используется namespace из конструктора.

# File lib/dapp/kube/kubernetes/client.rb, line 97
def with_namespace(namespace, &blk)
  old_namespace = @namespace
  begin
    @namespace = namespace
    return yield
  ensure
    @namespace = old_namespace
  end
end
with_query(query) { || ... } click to toggle source
# File lib/dapp/kube/kubernetes/client.rb, line 107
def with_query(query, &blk)
  old_query = @query_parameters
  begin
    @query_parameters = query
    return yield
  ensure
    @query_parameters = old_query
  end
end

Protected Instance Methods

kube_server_options(excon_parameters = {}) click to toggle source
# File lib/dapp/kube/kubernetes/client.rb, line 302
def kube_server_options(excon_parameters = {})
  {}.tap do |opts|
    context_config = config.context_config(context)
    user_config = config.user_config(context_config['user'])
    cluster_config = config.cluster_config(context_config['cluster'])

    client_cert = user_config['client-certificate']
    opts[:client_cert] = client_cert if client_cert

    client_cert_data= user_config['client-certificate-data']
    opts[:client_cert_data] = Base64.decode64(client_cert_data) if client_cert_data

    client_key = user_config['client-key']
    opts[:client_key] = client_key if client_key

    client_key_data = user_config['client-key-data']
    opts[:client_key_data] = Base64.decode64(client_key_data) if client_key_data

    ssl_cert_store = OpenSSL::X509::Store.new
    if ssl_ca_file = cluster_config['certificate-authority']
      ssl_cert_store.add_file ssl_ca_file
    elsif ssl_ca_data = cluster_config['certificate-authority-data']
      ssl_cert_store.add_cert OpenSSL::X509::Certificate.new(Base64.decode64(ssl_ca_data))
    end
    opts[:ssl_cert_store] = ssl_cert_store

    opts[:ssl_ca_file] = nil

    opts[:middlewares] = [*Excon.defaults[:middlewares], Excon::Middleware::RedirectFollower]

    opts.merge!(excon_parameters)
  end
end
load_body!(response, request_parameters, json: true) click to toggle source
# File lib/dapp/kube/kubernetes/client.rb, line 255
def load_body!(response, request_parameters, json: true)
  response_ok = response.status.to_s.start_with? '2'

  if response_ok
    if json
      JSON.parse(response.body)
    else
      response.body
    end
  else
    err_data = {}
    err_data[:response_http_status] = response.status
    err_data[:response_raw_body] = response.body
    if response_body = (JSON.parse(response.body) rescue nil)
      err_data[:response_body] = response_body
    end
    err_data[:request_parameters] = request_parameters

    if response.status.to_s.start_with? '5'
      raise Error::Default, code: :server_error, data: err_data
    elsif response.status.to_s == '404'
      case err_data.fetch(:response_body, {}).fetch('details', {})['kind']
      when 'pods'
        raise Error::Pod::NotFound, data: err_data
      else
        raise Error::NotFound, data: err_data
      end
    elsif not response.status.to_s.start_with? '2'
      raise Error::Base, code: :bad_request, data: err_data
    end
  end
end
request!(method, path, body: nil, excon_parameters: {}, response_body_parameters: {}, **query_parameters) click to toggle source

query_parameters — соответствует 'Query Parameters' в документации kubernetes excon_parameters — соответствует connection-опциям Excon body — hash для http-body, соответствует 'Body Parameters' в документации kubernetes, опционален

# File lib/dapp/kube/kubernetes/client.rb, line 237
def request!(method, path, body: nil, excon_parameters: {}, response_body_parameters: {}, **query_parameters)
  if method == :get
    excon_parameters[:idempotent] = true
    excon_parameters[:retry_limit] = 6
    excon_parameters[:retry_interval] = 5
  end

  excon_parameters[:connect_timeout] ||= timeout
  excon_parameters[:read_timeout] ||= timeout
  excon_parameters[:write_timeout] ||= timeout

  with_connection(excon_parameters: excon_parameters) do |conn|
    request_parameters = {method: method, path: path, query: @query_parameters.merge(query_parameters)}
    request_parameters[:body] = JSON.dump(body) if body
    load_body! conn.request(request_parameters), request_parameters, **response_body_parameters
  end
end
with_connection(excon_parameters: {}) { |connection| ... } click to toggle source
# File lib/dapp/kube/kubernetes/client.rb, line 288
def with_connection(excon_parameters: {}, &blk)
  connection = begin
    context_config = config.context_config(context)
    cluster_config = config.cluster_config(context_config['cluster'])
    Excon.new(cluster_config['server'], **kube_server_options(excon_parameters)).tap(&:get)
  rescue Excon::Error::Socket => err
    raise Error::ConnectionRefused,
          code: :server_connection_refused,
          data: { url: cluster_config['server'], error: err.message }
  end

  return yield connection
end