Module: Sensu::Plugins::Kubernetes::Client

Included in:
CLI
Defined in:
lib/sensu-plugins-kubernetes/client.rb

Overview

A mixin module that provides Kubernetes client (kubeclient) support.

Constant Summary collapse

INCLUSTER_CA_FILE =

The location of the service account provided CA. (if the cluster is configured to provide it)

'/var/run/secrets/kubernetes.io/serviceaccount/ca.crt'
INCLUSTER_TOKEN_FILE =

The location of the service account provided authentication token.

'/var/run/secrets/kubernetes.io/serviceaccount/token'

Instance Method Summary collapse

Instance Method Details

#kubeclient(options = {}) ⇒ Object

Creates a new Kubeclient::Client instance using the given SSL and authentication options (if any)

Parameters:

  • options (Hash) (defaults to: {})

    The Kubernetes API connection details.

Options Hash (options):

  • :server (String)

    URL to API server

  • :version (String)

    API version

  • :incluster (Boolean)

    Use service account authentication if true

  • :ca_file (String)

    CA file used to verify API server certificate

  • :client_cert_file (String)

    Client certificate file to present

  • :client_key_file (String)

    Client private key file for the client certificate

  • :username (String)

    Username with access to API

  • :password (String)

    If a username is passed, also pass a password

  • :token (String)

    The bearer token for Kubernetes authorization

  • :token_file (String)

    A file containing the bearer token for Kubernetes authorization

  • :kube_config (String)

    A file containing kubeconfig yaml configuration

Raises:

  • (ArgumentError)

    If an invalid option, or combination of options, is given.

  • (Errono::*)

    If there is a problem reading the client certificate or private key file.

  • (OpenSSL::X509::CertificateError)

    If there is a problem with the client certificate.

  • (OpenSSL::PKey::RSAError)

    If there is a problem with the client private key.



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/sensu-plugins-kubernetes/client.rb', line 50

def kubeclient(options = {})
  raise(ArgumentError, 'options must be a hash') unless options.is_a?(Hash)

  if options[:kube_config]
    begin
      config = Kubeclient::Config.read(options[:kube_config])

      api_server = config.context.api_endpoint
      api_version = config.context.api_version

      ssl_options = config.context.ssl_options
      auth_options = config.context.auth_options
    rescue StandardError => e
      raise e, "Unable to read kubeconfig: #{e}", e.backtrace
    end
  else
    api_server = options[:server]
    api_version = options[:version]

    ssl_options = {
      ca_file: options[:ca_file]
    }
    auth_options = {
      username: options[:username],
      password: options[:password],
      bearer_token: options[:token],
      bearer_token_file: options[:token_file]
    }
  end

  if %i[client_cert_file client_key_file].count { |k| options[k] } == 1
    raise ArgumentError, 'SSL requires both client cert and client key'
  end

  if options[:client_cert_file]
    begin
      ssl_options[:client_cert] = OpenSSL::X509::Certificate.new(File.read(options[:client_cert_file]))
    rescue StandardError => e
      raise e, "Unable to read client certificate: #{e}", e.backtrace
    end
  end

  if options[:client_key_file]
    begin
      ssl_options[:client_key] = OpenSSL::PKey::RSA.new(File.read(options[:client_key_file]))
    rescue StandardError => e
      raise e, "Unable to read client key: #{e}", e.backtrace
    end
  end

  if options[:incluster]
    # Provide in-cluster defaults, if not already specified
    # (following the kubernetes incluster config code, more or less)

    # api-server
    # TODO: use in-cluster DNS ??
    if api_server.nil?
      host = ENV['KUBERNETES_SERVICE_HOST']
      port = ENV['KUBERNETES_SERVICE_PORT']
      if host.nil? || port.nil?
        raise ArgumentError, 'unable to load in-cluster configuration,'\
             ' KUBERNETES_SERVICE_HOST and KUBERNETES_SERVICE_PORT'\
             ' must be defined'
      end
      api_server = URI::HTTPS.build(host: host, port: port, path: '/api')
    end

    # ca file, but only if it exists
    if ssl_options[:ca_file].nil? && File.exist?(INCLUSTER_CA_FILE)
      # Readability/permission issues should be left to kubeclient
      ssl_options[:ca_file] = INCLUSTER_CA_FILE
    end

    # token file
    if auth_options[:bearer_token_file].nil?
      auth_options[:bearer_token_file] = INCLUSTER_TOKEN_FILE
    end
  end

  ssl_options[:verify_ssl] = ssl_options[:ca_file] ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE

  begin
    # new only throws errors on bad arguments
    Kubeclient::Client.new(api_server, api_version,
                           ssl_options: ssl_options,
                           auth_options: auth_options)
  rescue URI::InvalidURIError => e
    # except for this one, which we'll re-wrap to make catching easier
    raise ArgumentError, "Invalid API server: #{e}", e.backtrace
  end
end