Class: K8s::Client

Inherits:
Object
  • Object
show all
Includes:
MonitorMixin
Defined in:
lib/k8s/client.rb,
lib/k8s/client/version.rb

Overview

Top-level client wrapper. Uses a Transport instance to talk to the kube API. Offers access to APIClient and ResourceClient instances.

Constant Summary collapse

VERSION =

Updated on releases using semver.

"0.10.4"

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(transport, namespace: nil) ⇒ Client

Returns a new instance of Client.

Parameters:

  • transport (K8s::Transport)
  • namespace (String, nil) (defaults to: nil)

    default namespace for all operations



108
109
110
111
112
113
114
# File 'lib/k8s/client.rb', line 108

def initialize(transport, namespace: nil)
  @transport = transport
  @namespace = namespace

  @api_clients = {}
  super()
end

Instance Attribute Details

#transportObject (readonly)

Returns the value of attribute transport.



104
105
106
# File 'lib/k8s/client.rb', line 104

def transport
  @transport
end

Class Method Details

.autoconfig(namespace: nil, **options) ⇒ K8s::Client

Attempts to create a K8s::Client instance automatically using environment variables, existing configuration files or in cluster configuration.

Look-up order:

- KUBE_TOKEN, KUBE_CA, KUBE_SERVER environment variables
- KUBECONFIG environment variable
- $HOME/.kube/config file
- In cluster configuration

Will raise when no means of configuration is available

Parameters:

  • options (Hash)

    default namespace for all operations

Returns:

Raises:

  • (K8s::Error::Config, Errno::ENOENT, Errno::EACCES)


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
# File 'lib/k8s/client.rb', line 70

def self.autoconfig(namespace: nil, **options)
  if ENV.values_at('KUBE_TOKEN', 'KUBE_CA', 'KUBE_SERVER').none? { |v| v.nil? || v.empty? }
    unless Base64.decode64(ENV['KUBE_CA']).match?(/CERTIFICATE/)
      raise ArgumentError, 'KUBE_CA does not seem to be base64 encoded'
    end

    begin
      token = options[:auth_token] || Base64.strict_decode64(ENV['KUBE_TOKEN'])
    rescue ArgumentError
      raise ArgumentError, 'KUBE_TOKEN does not seem to be base64 encoded'
    end

    configuration = K8s::Config.build(server: ENV['KUBE_SERVER'], ca: ENV['KUBE_CA'], auth_token: token)
  elsif !ENV['KUBECONFIG'].to_s.empty?
    configuration = K8s::Config.from_kubeconfig_env(ENV['KUBECONFIG'])
  else
    found_config = [
      File.join(Dir.home, '.kube', 'config'),
      '/etc/kubernetes/admin.conf',
      '/etc/kubernetes/kubelet.conf'
    ].find { |f| File.exist?(f) && File.readable?(f) }

    configuration = K8s::Config.load_file(found_config) if found_config
  end

  if configuration
    config(configuration, namespace: namespace, **options)
  else
    in_cluster_config(namespace: namespace, **options)
  end
end

.config(config, namespace: nil, **options) ⇒ K8s::Client

Parameters:

  • config (Phraos::Kube::Config)
  • namespace (String) (defaults to: nil)

    @see #initialize

  • options (Hash)

    @see Transport.config

Returns:



38
39
40
41
42
43
# File 'lib/k8s/client.rb', line 38

def self.config(config, namespace: nil, **options)
  new(
    Transport.config(config, **options),
    namespace: namespace
  )
end

.in_cluster_config(namespace: nil, **options) ⇒ K8s::Client

An K8s::Client instance from in-cluster config within a kube pod, using the kubernetes service envs and serviceaccount secrets

Parameters:

  • namespace (String) (defaults to: nil)

    default namespace for all operations

  • options (Hash)

    options passed to transport, @see Transport#in_cluster_config

Returns:

Raises:

  • (K8s::Error::Config, Errno::ENOENT, Errno::EACCES)

See Also:

  • Transport#in_cluster_config


52
53
54
# File 'lib/k8s/client.rb', line 52

def self.in_cluster_config(namespace: nil, **options)
  new(Transport.in_cluster_config(**options), namespace: namespace)
end

Instance Method Details

#api(api_version = 'v1') ⇒ APIClient

Parameters:

  • api_version (String) (defaults to: 'v1')

    “group/version” or “version” (core)

Returns:



124
125
126
# File 'lib/k8s/client.rb', line 124

def api(api_version = 'v1')
  @api_clients[api_version] ||= APIClient.new(@transport, api_version)
end

#api_groupsArray<String>

Cached /apis preferred group apiVersions

Returns:

  • (Array<String>)


147
148
149
# File 'lib/k8s/client.rb', line 147

def api_groups
  @api_groups || api_groups!
end

#api_groups!Array<String>

Force-update /apis cache. Required if creating new CRDs/apiservices.

Returns:

  • (Array<String>)


132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/k8s/client.rb', line 132

def api_groups!
  synchronize do
    @api_groups = @transport.get(
      '/apis',
      response_class: K8s::API::MetaV1::APIGroupList
    ).groups.flat_map{ |api_group| api_group.versions.map(&:groupVersion) }

    @api_clients.clear
  end

  @api_groups
end

#apis(api_versions = nil, prefetch_resources: false, skip_missing: false) ⇒ Array<APIClient>

Parameters:

  • api_versions (Array<String>) (defaults to: nil)

    defaults to all APIs

  • prefetch_resources (Boolean) (defaults to: false)

    prefetch any missing api_resources for each api_version

  • skip_missing (Boolean) (defaults to: false)

    return APIClient without api_resources? if 404

Returns:



155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# File 'lib/k8s/client.rb', line 155

def apis(api_versions = nil, prefetch_resources: false, skip_missing: false)
  api_versions ||= ['v1'] + api_groups

  if prefetch_resources
    # api groups that are missing their api_resources
    api_paths = api_versions
                .uniq
                .reject{ |api_version| api(api_version).api_resources? }
                .map{ |api_version| APIClient.path(api_version) }

    # load into APIClient.api_resources=
    begin
      @transport.gets(*api_paths, response_class: K8s::API::MetaV1::APIResourceList, skip_missing: skip_missing).each do |api_resource_list|
        api(api_resource_list.groupVersion).api_resources = api_resource_list.resources if api_resource_list
      end
    rescue K8s::Error::NotFound, K8s::Error::ServiceUnavailable # rubocop:disable Lint/HandleExceptions
      # kubernetes api is in unstable state
      # because this is only performance optimization, better to skip prefetch and move on
    end
  end

  api_versions.map{ |api_version| api(api_version) }
end

#client_for_resource(resource, namespace: nil) ⇒ K8s::ResourceClient

Parameters:

  • resource (K8s::Resource)
  • namespace (String, nil) (defaults to: nil)

    default if resource is missing namespace

Returns:

Raises:



218
219
220
# File 'lib/k8s/client.rb', line 218

def client_for_resource(resource, namespace: nil)
  api(resource.apiVersion).client_for_resource(resource, namespace: namespace)
end

#create_resource(resource) ⇒ K8s::Resource

Parameters:

Returns:



224
225
226
# File 'lib/k8s/client.rb', line 224

def create_resource(resource)
  client_for_resource(resource).create_resource(resource)
end

#delete_resource(resource, **options) ⇒ K8s::Resource

Parameters:

Returns:

See Also:



272
273
274
# File 'lib/k8s/client.rb', line 272

def delete_resource(resource, **options)
  client_for_resource(resource).delete_resource(resource, **options)
end

#get_resource(resource) ⇒ K8s::Resource

Parameters:

Returns:



230
231
232
# File 'lib/k8s/client.rb', line 230

def get_resource(resource)
  client_for_resource(resource).get_resource(resource)
end

#get_resources(resources) ⇒ Array<K8s::Resource, nil>

Returns nils for any resources that do not exist. This includes custom resources that were not yet defined.

Parameters:

Returns:



239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
# File 'lib/k8s/client.rb', line 239

def get_resources(resources)
  # prefetch api resources, skip missing APIs
  resource_apis = apis(resources.map(&:apiVersion), prefetch_resources: true, skip_missing: true)

  # map each resource to excon request options, or nil if resource is not (yet) defined
  requests = resources.zip(resource_apis).map{ |resource, api_client|
    next nil unless api_client.api_resources?

    resource_client = api_client.client_for_resource(resource)

    {
      method: 'GET',
      path: resource_client.path(resource..name, namespace: resource..namespace),
      response_class: resource_client.resource_class
    }
  }

  # map non-nil requests to response objects, or nil for nil request options
  Util.compact_map(requests) { |reqs|
    @transport.requests(*reqs, skip_missing: true)
  }
end

#list_resources(resources = nil, **options) ⇒ Array<K8s::Resource>

Pipeline list requests for multiple resource types.

Returns flattened array with mixed resource kinds.

Parameters:

  • resources (Array<K8s::ResourceClient>) (defaults to: nil)

    default is all listable resources for api

  • options

    @see K8s::ResourceClient#list

Returns:



198
199
200
201
202
203
204
205
206
207
208
209
210
211
# File 'lib/k8s/client.rb', line 198

def list_resources(resources = nil, **options)
  cached_clients = @api_clients.size.positive?
  resources ||= self.resources.select(&:list?)

  begin
    ResourceClient.list(resources, @transport, **options)
  rescue K8s::Error::NotFound
    raise unless cached_clients

    cached_clients = false
    api_groups!
    retry
  end
end

#patch_resource(resource, attrs) ⇒ K8s::Client

Parameters:

Returns:



279
280
281
# File 'lib/k8s/client.rb', line 279

def patch_resource(resource, attrs)
  client_for_resource(resource).json_patch(resource..name, attrs)
end

#resources(namespace: nil) ⇒ Array<K8s::ResourceClient>

Parameters:

  • namespace (String, nil) (defaults to: nil)

Returns:



181
182
183
184
185
186
187
188
189
# File 'lib/k8s/client.rb', line 181

def resources(namespace: nil)
  apis(prefetch_resources: true).map { |api|
    begin
      api.resources(namespace: namespace)
    rescue K8s::Error::ServiceUnavailable, K8s::Error::NotFound
      []
    end
  }.flatten
end

#update_resource(resource) ⇒ K8s::Resource

Parameters:

Returns:



264
265
266
# File 'lib/k8s/client.rb', line 264

def update_resource(resource)
  client_for_resource(resource).update_resource(resource)
end

#versionK8s::API::Version

Returns:

Raises:



118
119
120
# File 'lib/k8s/client.rb', line 118

def version
  @version ||= @transport.version
end