Common methods this is mixed in by other gems
# File lib/kubeclient/common.rb, line 165 def initialize(hash = nil, args = {}) args[:recurse_over_arrays] = true super(hash, args) end
# File lib/kubeclient/common.rb, line 126 def self.parse_definition(kind, name) # "name": "componentstatuses", networkpolicies, endpoints # "kind": "ComponentStatus" NetworkPolicy, Endpoints # maintain pre group api compatibility for endpoints. # See: https://github.com/kubernetes/kubernetes/issues/8115 kind = 'Endpoint' if kind == 'Endpoints' prefix = kind[0..kind.rindex(/[A-Z]/)] # NetworkP m = name.match(/^#{prefix.downcase}(.*)$/) m && OpenStruct.new( entity_type: kind, # ComponentStatus resource_name: name, # componentstatuses method_names: [ ClientMixin.underscore_entity(kind), # component_status ClientMixin.underscore_entity(prefix) + m[1] # component_statuses ] ) end
# File lib/kubeclient/common.rb, line 158 def self.resource_class(class_owner, entity_type) if class_owner.const_defined?(entity_type, false) class_owner.const_get(entity_type, false) else class_owner.const_set( entity_type, Class.new(RecursiveOpenStruct) do def initialize(hash = nil, args = {}) args[:recurse_over_arrays] = true super(hash, args) end end ) end end
# File lib/kubeclient/common.rb, line 435 def self.restclient_read_timeout_option @restclient_read_timeout_option ||= # RestClient silently accepts unknown options, so check accessors instead. if RestClient::Resource.instance_methods.include?(:read_timeout) # rest-client 2.0 :read_timeout elsif RestClient::Resource.instance_methods.include?(:timeout) # rest-client 1.x :timeout else fail ArgumentError("RestClient doesn't support neither :read_timeout nor :timeout") end end
# File lib/kubeclient/common.rb, line 214 def self.underscore_entity(entity_name) entity_name.gsub(/([a-z])([A-Z])/, '\1_\2').downcase end
# File lib/kubeclient/common.rb, line 357 def all_entities discover unless @discovered @entities.values.each_with_object({}) do |entity, result_hash| # method call for get each entities # build hash of entity name to array of the entities method_name = "get_#{entity.method_names[1]}" begin result_hash[entity.method_names[0]] = send(method_name) rescue KubeException next # do not fail due to resources not supporting get end end end
# File lib/kubeclient/common.rb, line 430 def api response = handle_exception { create_rest_client.get(@headers) } JSON.parse(response) end
# File lib/kubeclient/common.rb, line 423 def api_valid? result = api result.is_a?(Hash) && (result['versions'] || []).any? do |group| @api_group.empty? ? group.include?(@api_version) : group['version'] == (@api_version) end end
# File lib/kubeclient/common.rb, line 154 def build_namespace_prefix(namespace) namespace.to_s.empty? ? '' : "namespaces/#{namespace}/" end
# File lib/kubeclient/common.rb, line 307 def create_entity(entity_type, resource_name, entity_config, klass) # Duplicate the entity_config to a hash so that when we assign # kind and apiVersion, this does not mutate original entity_config obj. hash = entity_config.to_hash ns_prefix = build_namespace_prefix(hash[:metadata][:namespace]) # TODO: temporary solution to add "kind" and apiVersion to request # until this issue is solved # https://github.com/GoogleCloudPlatform/kubernetes/issues/6439 # TODO: #2 solution for # https://github.com/kubernetes/kubernetes/issues/8115 if entity_type.eql? 'Endpoint' hash[:kind] = 'Endpoints' else hash[:kind] = entity_type end hash[:apiVersion] = @api_group + @api_version response = handle_exception do rest_client[ns_prefix + resource_name] .post(hash.to_json, { 'Content-Type' => 'application/json' }.merge(@headers)) end result = JSON.parse(response) new_entity(result, klass) end
# File lib/kubeclient/common.rb, line 218 def create_rest_client(path = nil) path ||= @api_endpoint.path options = { ssl_ca_file: @ssl_options[:ca_file], ssl_cert_store: @ssl_options[:cert_store], verify_ssl: @ssl_options[:verify_ssl], ssl_client_cert: @ssl_options[:client_cert], ssl_client_key: @ssl_options[:client_key], proxy: @http_proxy_uri, user: @auth_options[:username], password: @auth_options[:password], open_timeout: @timeouts[:open], ClientMixin.restclient_read_timeout_option => @timeouts[:read] } RestClient::Resource.new(@api_endpoint.merge(path).to_s, options) end
# File lib/kubeclient/common.rb, line 174 def define_entity_methods @entities.values.each do |entity| klass = ClientMixin.resource_class(@class_owner, entity.entity_type) # get all entities of a type e.g. get_nodes, get_pods, etc. define_singleton_method("get_#{entity.method_names[1]}") do |options = {}| get_entities(entity.entity_type, klass, entity.resource_name, options) end # watch all entities of a type e.g. watch_nodes, watch_pods, etc. define_singleton_method("watch_#{entity.method_names[1]}") do |options = {}| # This method used to take resource_version as a param, so # this conversion is to keep backwards compatibility options = { resource_version: options } unless options.is_a?(Hash) watch_entities(entity.resource_name, options) end # get a single entity of a specific type by name define_singleton_method("get_#{entity.method_names[0]}") do |name, namespace = nil| get_entity(klass, entity.resource_name, name, namespace) end define_singleton_method("delete_#{entity.method_names[0]}") do |name, namespace = nil| delete_entity(entity.resource_name, name, namespace) end define_singleton_method("create_#{entity.method_names[0]}") do |entity_config| create_entity(entity.entity_type, entity.resource_name, entity_config, klass) end define_singleton_method("update_#{entity.method_names[0]}") do |entity_config| update_entity(entity.resource_name, entity_config) end define_singleton_method("patch_#{entity.method_names[0]}") do |name, patch, namespace = nil| patch_entity(entity.resource_name, name, patch, namespace) end end end
# File lib/kubeclient/common.rb, line 299 def delete_entity(resource_name, name, namespace = nil) ns_prefix = build_namespace_prefix(namespace) handle_exception do rest_client[ns_prefix + resource_name + "/#{name}"] .delete(@headers) end end
# File lib/kubeclient/common.rb, line 120 def discover load_entities define_entity_methods @discovered = true end
# File lib/kubeclient/common.rb, line 104 def discovery_needed?(method_sym) !@discovered && ENTITY_METHODS.any? { |x| method_sym.to_s.start_with?(x) } end
Accepts the following string options:
:namespace - the namespace of the entity. :label_selector - a selector to restrict the list of returned objects by their labels. :field_selector - a selector to restrict the list of returned objects by their fields.
# File lib/kubeclient/common.rb, line 265 def get_entities(entity_type, klass, resource_name, options = {}) params = {} SEARCH_ARGUMENTS.each { |k, v| params[k] = options[v] if options[v] } ns_prefix = build_namespace_prefix(options[:namespace]) response = handle_exception do rest_client[ns_prefix + resource_name] .get({ 'params' => params }.merge(@headers)) end result = JSON.parse(response) resource_version = result.fetch('resourceVersion', nil) if resource_version.nil? resource_version = result.fetch('metadata', {}).fetch('resourceVersion', nil) end # result['items'] might be nil due to https://github.com/kubernetes/kubernetes/issues/13096 collection = result['items'].to_a.map { |item| new_entity(item, klass) } Kubeclient::Common::EntityList.new(entity_type, resource_version, collection) end
# File lib/kubeclient/common.rb, line 289 def get_entity(klass, resource_name, name, namespace = nil) ns_prefix = build_namespace_prefix(namespace) response = handle_exception do rest_client[ns_prefix + resource_name + "/#{name}"] .get(@headers) end result = JSON.parse(response) new_entity(result, klass) end
# File lib/kubeclient/common.rb, line 371 def get_pod_log(pod_name, namespace, container: nil, previous: false) params = {} params[:previous] = true if previous params[:container] = container if container ns = build_namespace_prefix(namespace) handle_exception do rest_client[ns + "pods/#{pod_name}/log"] .get({ 'params' => params }.merge(@headers)) end end
# File lib/kubeclient/common.rb, line 108 def handle_exception yield rescue RestClient::Exception => e json_error_msg = begin JSON.parse(e.response || '') || {} rescue JSON::ParserError {} end err_message = json_error_msg['message'] || e.message raise KubeException.new(e.http_code, err_message, e.response) end
# File lib/kubeclient/common.rb, line 145 def handle_uri(uri, path) fail ArgumentError, 'Missing uri' unless uri @api_endpoint = (uri.is_a?(URI) ? uri : URI.parse(uri)) @api_endpoint.path = path if @api_endpoint.path.empty? @api_endpoint.path = @api_endpoint.path.chop if @api_endpoint.path.end_with? '/' components = @api_endpoint.path.to_s.split('/') # ["", "api"] or ["", "apis", batch] @api_group = components.length > 2 ? components[2] + '/' : '' end
# File lib/kubeclient/common.rb, line 51 def initialize_client( class_owner, uri, path, version, ssl_options: DEFAULT_SSL_OPTIONS, auth_options: DEFAULT_AUTH_OPTIONS, socket_options: DEFAULT_SOCKET_OPTIONS, timeouts: DEFAULT_TIMEOUTS, http_proxy_uri: DEFAULT_HTTP_PROXY_URI ) validate_auth_options(auth_options) handle_uri(uri, path) @class_owner = class_owner @entities = {} @discovered = false @api_version = version @headers = {} @ssl_options = ssl_options @auth_options = auth_options @socket_options = socket_options # Allow passing partial timeouts hash, without unspecified # @timeouts[:foo] == nil resulting in infinite timeout. @timeouts = DEFAULT_TIMEOUTS.merge(timeouts) @http_proxy_uri = http_proxy_uri.to_s if http_proxy_uri if auth_options[:bearer_token] bearer_token(@auth_options[:bearer_token]) elsif auth_options[:bearer_token_file] validate_bearer_token_file bearer_token(File.read(@auth_options[:bearer_token_file])) end end
# File lib/kubeclient/common.rb, line 86 def method_missing(method_sym, *args, &block) if discovery_needed?(method_sym) discover send(method_sym, *args, &block) else super end end
# File lib/kubeclient/common.rb, line 353 def new_entity(hash, klass) klass.new(hash) end
# File lib/kubeclient/common.rb, line 342 def patch_entity(resource_name, name, patch, namespace = nil) ns_prefix = build_namespace_prefix(namespace) handle_exception do rest_client[ns_prefix + resource_name + "/#{name}"] .patch( patch.to_json, { 'Content-Type' => 'application/strategic-merge-patch+json' }.merge(@headers) ) end end
# File lib/kubeclient/common.rb, line 414 def process_template(template) ns_prefix = build_namespace_prefix(template[:metadata][:namespace]) response = handle_exception do rest_client[ns_prefix + 'processedtemplates'] .post(template.to_h.to_json, { 'Content-Type' => 'application/json' }.merge(@headers)) end JSON.parse(response) end
# File lib/kubeclient/common.rb, line 398 def proxy_url(kind, name, port, namespace = '') discover unless @discovered entity_name_plural = if %w(services pods nodes).include?(kind.to_s) kind.to_s else @entities[kind.to_s].resource_name end ns_prefix = build_namespace_prefix(namespace) # TODO: Change this once services supports the new scheme if entity_name_plural == 'pods' rest_client["#{ns_prefix}#{entity_name_plural}/#{name}:#{port}/proxy"].url else rest_client["proxy/#{ns_prefix}#{entity_name_plural}/#{name}:#{port}"].url end end
# File lib/kubeclient/common.rb, line 95 def respond_to_missing?(method_sym, include_private = false) if discovery_needed?(method_sym) discover respond_to?(method_sym, include_private) else super end end
# File lib/kubeclient/common.rb, line 235 def rest_client @rest_client ||= begin create_rest_client("#{@api_endpoint.path}/#{@api_version}") end end
# File lib/kubeclient/common.rb, line 333 def update_entity(resource_name, entity_config) name = entity_config[:metadata][:name] ns_prefix = build_namespace_prefix(entity_config[:metadata][:namespace]) handle_exception do rest_client[ns_prefix + resource_name + "/#{name}"] .put(entity_config.to_h.to_json, { 'Content-Type' => 'application/json' }.merge(@headers)) end end
Accepts the following string options:
:namespace - the namespace of the entity. :name - the name of the entity to watch. :label_selector - a selector to restrict the list of returned objects by their labels. :field_selector - a selector to restrict the list of returned objects by their fields. :resource_version - shows changes that occur after that particular version of a resource.
# File lib/kubeclient/common.rb, line 247 def watch_entities(resource_name, options = {}) ns = build_namespace_prefix(options[:namespace]) path = "watch/#{ns}#{resource_name}" path += "/#{options[:name]}" if options[:name] uri = @api_endpoint.merge("#{@api_endpoint.path}/#{@api_version}/#{path}") params = {} WATCH_ARGUMENTS.each { |k, v| params[k] = options[v] if options[v] } uri.query = URI.encode_www_form(params) if params.any? Kubeclient::Common::WatchStream.new(uri, http_options(uri)) end
# File lib/kubeclient/common.rb, line 383 def watch_pod_log(pod_name, namespace, container: nil) # Adding the "follow=true" query param tells the Kubernetes API to keep # the connection open and stream updates to the log. params = { follow: true } params[:container] = container if container ns = build_namespace_prefix(namespace) uri = @api_endpoint.dup uri.path += "/#{@api_version}/#{ns}pods/#{pod_name}/log" uri.query = URI.encode_www_form(params) Kubeclient::Common::WatchStream.new(uri, http_options(uri), format: :text) end
# File lib/kubeclient/common.rb, line 464 def bearer_token(bearer_token) @headers ||= {} @headers[:Authorization] = "Bearer #{bearer_token}" end
# File lib/kubeclient/common.rb, line 460 def fetch_entities JSON.parse(handle_exception { rest_client.get(@headers) }) end
# File lib/kubeclient/common.rb, line 489 def http_options(uri) options = { basic_auth_user: @auth_options[:username], basic_auth_password: @auth_options[:password], headers: @headers, http_proxy_uri: @http_proxy_uri } if uri.scheme == 'https' options[:ssl] = { ca_file: @ssl_options[:ca_file], cert: @ssl_options[:client_cert], cert_store: @ssl_options[:cert_store], key: @ssl_options[:client_key], # ruby HTTP uses verify_mode instead of verify_ssl # http://ruby-doc.org/stdlib-1.9.3/libdoc/openssl/rdoc/OpenSSL/SSL/SSLContext.html verify_mode: @ssl_options[:verify_ssl] } end options.merge(@socket_options) end
# File lib/kubeclient/common.rb, line 449 def load_entities @entities = {} fetch_entities['resources'].each do |resource| next if resource['name'].include?('/') resource['kind'] = Kubeclient::Common::MissingKindCompatibility .resource_kind(resource['name']) if resource['kind'].nil? entity = ClientMixin.parse_definition(resource['kind'], resource['name']) @entities[entity.method_names[0]] = entity if entity end end
# File lib/kubeclient/common.rb, line 469 def validate_auth_options(opts) # maintain backward compatibility: opts[:username] = opts[:user] if opts[:user] if [:bearer_token, :bearer_token_file, :username].count { |key| opts[key] } > 1 fail(ArgumentError, 'Invalid auth options: specify only one of username/password,' ' bearer_token or bearer_token_file') elsif [:username, :password].count { |key| opts[key] } == 1 fail(ArgumentError, 'Basic auth requires both username & password') end end
# File lib/kubeclient/common.rb, line 481 def validate_bearer_token_file msg = "Token file #{@auth_options[:bearer_token_file]} does not exist" fail ArgumentError, msg unless File.file?(@auth_options[:bearer_token_file]) msg = "Cannot read token file #{@auth_options[:bearer_token_file]}" fail ArgumentError, msg unless File.readable?(@auth_options[:bearer_token_file]) end