kubeclient 4.2.2 → 4.3.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of kubeclient might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.rubocop.yml +4 -0
- data/CHANGELOG.md +10 -0
- data/README.md +73 -24
- data/kubeclient.gemspec +2 -0
- data/lib/kubeclient.rb +1 -0
- data/lib/kubeclient/common.rb +18 -5
- data/lib/kubeclient/config.rb +8 -0
- data/lib/kubeclient/google_application_default_credentials.rb +11 -1
- data/lib/kubeclient/oidc_auth_provider.rb +46 -0
- data/lib/kubeclient/version.rb +1 -1
- data/test/config/gcpauth.kubeconfig +22 -0
- data/test/config/oidcauth.kubeconfig +25 -0
- data/test/json/service_json_patch.json +26 -0
- data/test/json/service_merge_patch.json +26 -0
- data/test/test_config.rb +37 -0
- data/test/test_helper.rb +1 -0
- data/test/test_oidc_auth_provider.rb +84 -0
- data/test/test_service.rb +54 -0
- metadata +41 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f53d064748148a041be5853562e7789f8cc792735e4916c1859c63bf93f1f8bc
|
4
|
+
data.tar.gz: 15a4ba1b298c4bfb3469765a92ad58e8dd806f470870bbadaaf81a0c7ac3dbf4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 39b85eead8370920f1dbf6f30720506de756894ae4d34b55a5e589b9ff1c8bce0a6e93c5517d4afbc7bb039b729e760250c7b4ead17b4fced76f52941b6bcf44
|
7
|
+
data.tar.gz: 6c95743f7680fac757b90804050b81488d084fe083749890f069ca8ed1a97158c789d3a912fcccc72f64d089695a9fe0c2085b45dffa492773c82d2ae5b3626d
|
data/.rubocop.yml
CHANGED
@@ -14,12 +14,16 @@ Metrics/ParameterLists:
|
|
14
14
|
CountKeywordArgs: false
|
15
15
|
Metrics/CyclomaticComplexity:
|
16
16
|
Max: 8
|
17
|
+
Metrics/PerceivedComplexity:
|
18
|
+
Max: 8
|
17
19
|
Metrics/ModuleLength:
|
18
20
|
Enabled: false
|
19
21
|
Style/MethodCallWithArgsParentheses:
|
20
22
|
Enabled: true
|
21
23
|
IgnoredMethods: [require, raise, include, attr_reader, refute, assert]
|
22
24
|
Exclude: [Gemfile, Rakefile, kubeclient.gemspec, Gemfile.dev.rb]
|
25
|
+
Metrics/BlockLength:
|
26
|
+
Exclude: [kubeclient.gemspec]
|
23
27
|
Security/MarshalLoad:
|
24
28
|
Exclude: [test/**/*]
|
25
29
|
Style/FileName:
|
data/CHANGELOG.md
CHANGED
@@ -4,6 +4,16 @@ Notable changes to this project will be documented in this file.
|
|
4
4
|
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
|
5
5
|
Kubeclient release versioning follows [SemVer](https://semver.org/).
|
6
6
|
|
7
|
+
## 4.3.0 — 2019-03-03
|
8
|
+
|
9
|
+
### Changed
|
10
|
+
- `GoogleApplicationDefaultCredentials` will automatically be used in `fetch_user_auth_options` if the `user[auth-provider][name] == 'gcp'` in the provided context. Note that `user[exec]` is checked first in anticipation of this functionality being added to GCP sometime in the future. Kubeclient _does not_ import the required `googleauth` gem, so you will need to import it in your calling application. (#394)
|
11
|
+
|
12
|
+
### Added
|
13
|
+
- OpenID Connect credentials will automatically be used if the `user[auth-provider][name] == 'oidc'` in the provided context. Note that `user[exec]` is checked first. Kubeclient _does not_ import the required `openid_connect` gem, so you will need to import it in your calling application. (#396)
|
14
|
+
|
15
|
+
- Support for `json_patch_#{entity}` and `merge_patch_#{entity}`. `patch_#{entity}` will continue to use strategic merge patch. (#390)
|
16
|
+
|
7
17
|
## 4.2.2 — 2019-01-09
|
8
18
|
|
9
19
|
### Added
|
data/README.md
CHANGED
@@ -166,29 +166,6 @@ namespace = File.read('/var/run/secrets/kubernetes.io/serviceaccount/namespace')
|
|
166
166
|
```
|
167
167
|
You can find information about tokens in [this guide](https://kubernetes.io/docs/tasks/access-application-cluster/access-cluster/#accessing-the-api-from-a-pod) and in [this reference](http://kubernetes.io/docs/admin/authentication/).
|
168
168
|
|
169
|
-
#### Google's Application Default Credentials
|
170
|
-
|
171
|
-
On Google Compute Engine, Google App Engine, or Google Cloud Functions, as well as `gcloud`-configured systems
|
172
|
-
with [application default credentials](https://developers.google.com/identity/protocols/application-default-credentials),
|
173
|
-
you can use the token provider to authorize `kubeclient`.
|
174
|
-
|
175
|
-
This requires the [`googleauth` gem](https://github.com/google/google-auth-library-ruby) that is _not_ included in
|
176
|
-
`kubeclient` dependencies so you should add it to your bundle.
|
177
|
-
|
178
|
-
```ruby
|
179
|
-
require 'googleauth'
|
180
|
-
|
181
|
-
auth_options = {
|
182
|
-
bearer_token: Kubeclient::GoogleApplicationDefaultCredentials.token
|
183
|
-
}
|
184
|
-
client = Kubeclient::Client.new(
|
185
|
-
'https://localhost:8443/api/', 'v1', auth_options: auth_options
|
186
|
-
)
|
187
|
-
```
|
188
|
-
|
189
|
-
Note that this token is good for one hour. If your code runs for longer than that, you should plan to
|
190
|
-
acquire a new one.
|
191
|
-
|
192
169
|
### Non-blocking IO
|
193
170
|
|
194
171
|
You can also use kubeclient with non-blocking sockets such as Celluloid::IO, see [here](https://github.com/httprb/http/wiki/Parallel-requests-with-Celluloid%3A%3AIO)
|
@@ -303,12 +280,82 @@ context = config.context('default/192-168-99-100:8443/system:admin')
|
|
303
280
|
|
304
281
|
Kubeclient::Client.new(
|
305
282
|
context.api_endpoint,
|
306
|
-
|
283
|
+
'v1',
|
307
284
|
ssl_options: context.ssl_options,
|
308
285
|
auth_options: context.auth_options
|
309
286
|
)
|
310
287
|
```
|
311
288
|
|
289
|
+
|
290
|
+
#### Google's Application Default Credentials
|
291
|
+
|
292
|
+
On Google Compute Engine, Google App Engine, or Google Cloud Functions, as well as `gcloud`-configured systems
|
293
|
+
with [application default credentials](https://developers.google.com/identity/protocols/application-default-credentials),
|
294
|
+
kubeclient can use `googleauth` gem to authorize.
|
295
|
+
|
296
|
+
This requires the [`googleauth` gem](https://github.com/google/google-auth-library-ruby) that is _not_ included in
|
297
|
+
`kubeclient` dependencies so you should add it to your bundle.
|
298
|
+
|
299
|
+
If you use `Config.context(...).auth_options` and the kubeconfig file has `user: {auth-provider: {name: gcp}}`, kubeclient will automatically try this (raising LoadError if you don't have `googleauth` in your bundle).
|
300
|
+
|
301
|
+
Or you can obtain a token manually:
|
302
|
+
|
303
|
+
```ruby
|
304
|
+
require 'googleauth'
|
305
|
+
|
306
|
+
auth_options = {
|
307
|
+
bearer_token: Kubeclient::GoogleApplicationDefaultCredentials.token
|
308
|
+
}
|
309
|
+
client = Kubeclient::Client.new(
|
310
|
+
'https://localhost:8443/api/', 'v1', auth_options: auth_options
|
311
|
+
)
|
312
|
+
```
|
313
|
+
|
314
|
+
Note that this returns a token good for one hour. If your code requires authorization for longer than that, you should plan to
|
315
|
+
acquire a new one, see [How to manually renew](#how-to-manually-renew-expired-credentials) section.
|
316
|
+
|
317
|
+
#### OIDC Auth Provider
|
318
|
+
|
319
|
+
If the cluster you are using has OIDC authentication enabled you can use the `openid_connect` gem to obtain
|
320
|
+
id-tokens if the one in your kubeconfig has expired.
|
321
|
+
|
322
|
+
This requires the [`openid_connect` gem](https://github.com/nov/openid_connect) which is not included in
|
323
|
+
the `kubeclient` dependencies so should be added to your own applications bundle.
|
324
|
+
|
325
|
+
The OIDC Auth Provider will not perform the initial setup of your `$KUBECONFIG` file. You will need to use something
|
326
|
+
like [`dexter`](https://github.com/gini/dexter) in order to configure the auth-provider in your `$KUBECONFIG` file.
|
327
|
+
|
328
|
+
If you use `Config.context(...).auth_options` and the `$KUBECONFIG` file has user: `{auth-provider: {name: oidc}}`,
|
329
|
+
kubeclient will automatically obtain a token (or use `id-token` if still valid)
|
330
|
+
|
331
|
+
Tokens are typically short-lived (e.g. 1 hour) and the expiration time is determined by the OIDC Provider (e.g. Google).
|
332
|
+
If your code requires authentication for longer than that you should obtain a new token periodically, see [How to manually renew](#how-to-manually-renew-expired-credentials) section.
|
333
|
+
|
334
|
+
Note: id-tokens retrieved via this provider are not written back to the `$KUBECONFIG` file as they would be when
|
335
|
+
using `kubectl`.
|
336
|
+
|
337
|
+
#### How to manually renew expired credentials
|
338
|
+
|
339
|
+
Kubeclient [does not yet](https://github.com/abonas/kubeclient/issues/393) help with this.
|
340
|
+
|
341
|
+
The division of labor between `Config` and `Context` objects may change, for now please make no assumptions at which stage `exec:` and `auth-provider:` are handled and whether they're cached.
|
342
|
+
The currently guaranteed way to renew is create a new `Config` object.
|
343
|
+
|
344
|
+
The more painful part is that you'll then need to create new `Client` object(s) with the credentials from new config.
|
345
|
+
So repeat all of this:
|
346
|
+
```ruby
|
347
|
+
config = Kubeclient::Config.read(ENV['KUBECONFIG'] || '/path/to/.kube/config')
|
348
|
+
context = config.context
|
349
|
+
ssl_options = context.ssl_options
|
350
|
+
auth_options = context.auth_options
|
351
|
+
|
352
|
+
client = Kubeclient::Client.new(
|
353
|
+
context.api_endpoint, 'v1',
|
354
|
+
ssl_options: ssl_options, auth_options: auth_options
|
355
|
+
)
|
356
|
+
# and additional Clients if needed...
|
357
|
+
```
|
358
|
+
|
312
359
|
#### Security: Don't use config from untrusted sources
|
313
360
|
|
314
361
|
`Config.read` is catastrophically unsafe — it will execute arbitrary command lines specified by the config!
|
@@ -490,6 +537,8 @@ The below example is for v1
|
|
490
537
|
patched = client.patch_pod("docker-registry", {metadata: {annotations: {key: 'value'}}}, "default")
|
491
538
|
```
|
492
539
|
|
540
|
+
`patch_#{entity}` is called using a [strategic merge patch](https://kubernetes.io/docs/tasks/run-application/update-api-object-kubectl-patch/#notes-on-the-strategic-merge-patch). `json_patch_#{entity}` and `merge_patch_#{entity}` are also available that use JSON patch and JSON merge patch, respectively. These strategies are useful for resources that do not support strategic merge patch, such as Custom Resources. Consult the [Kubernetes docs](https://kubernetes.io/docs/tasks/run-application/update-api-object-kubectl-patch/#use-a-json-merge-patch-to-update-a-deployment) for more information about the different patch strategies.
|
541
|
+
|
493
542
|
#### Get all entities of all types : all_entities
|
494
543
|
Returns a hash with the following keys (node, secret, service, pod, replication_controller, namespace, resource_quota, limit_range, endpoint, event, persistent_volume, persistent_volume_claim, component_status and service_account). Each key points to an EntityList of same type.
|
495
544
|
This method is a convenience method instead of calling each entity's get method separately.
|
data/kubeclient.gemspec
CHANGED
@@ -28,6 +28,8 @@ Gem::Specification.new do |spec|
|
|
28
28
|
spec.add_development_dependency 'vcr'
|
29
29
|
spec.add_development_dependency 'rubocop', '= 0.49.1'
|
30
30
|
spec.add_development_dependency 'googleauth', '~> 0.5.1'
|
31
|
+
spec.add_development_dependency('mocha', '~> 1.5')
|
32
|
+
spec.add_development_dependency 'openid_connect', '~> 1.1'
|
31
33
|
|
32
34
|
spec.add_dependency 'rest-client', '~> 2.0'
|
33
35
|
spec.add_dependency 'recursive-open-struct', '~> 1.0', '>= 1.0.4'
|
data/lib/kubeclient.rb
CHANGED
@@ -8,6 +8,7 @@ require 'kubeclient/google_application_default_credentials'
|
|
8
8
|
require 'kubeclient/exec_credentials'
|
9
9
|
require 'kubeclient/http_error'
|
10
10
|
require 'kubeclient/missing_kind_compatibility'
|
11
|
+
require 'kubeclient/oidc_auth_provider'
|
11
12
|
require 'kubeclient/resource'
|
12
13
|
require 'kubeclient/resource_not_found_error'
|
13
14
|
require 'kubeclient/version'
|
data/lib/kubeclient/common.rb
CHANGED
@@ -5,7 +5,7 @@ module Kubeclient
|
|
5
5
|
# Common methods
|
6
6
|
# this is mixed in by other gems
|
7
7
|
module ClientMixin
|
8
|
-
ENTITY_METHODS = %w[get watch delete create update patch].freeze
|
8
|
+
ENTITY_METHODS = %w[get watch delete create update patch json_patch merge_patch].freeze
|
9
9
|
|
10
10
|
DEFAULT_SSL_OPTIONS = {
|
11
11
|
client_cert: nil,
|
@@ -203,6 +203,7 @@ module Kubeclient
|
|
203
203
|
namespace.to_s.empty? ? '' : "namespaces/#{namespace}/"
|
204
204
|
end
|
205
205
|
|
206
|
+
# rubocop:disable Metrics/BlockLength
|
206
207
|
def define_entity_methods
|
207
208
|
@entities.values.each do |entity|
|
208
209
|
# get all entities of a type e.g. get_nodes, get_pods, etc.
|
@@ -238,11 +239,23 @@ module Kubeclient
|
|
238
239
|
update_entity(entity.resource_name, entity_config)
|
239
240
|
end
|
240
241
|
|
241
|
-
define_singleton_method("patch_#{entity.method_names[0]}")
|
242
|
-
|
242
|
+
define_singleton_method("patch_#{entity.method_names[0]}") \
|
243
|
+
do |name, patch, namespace = nil|
|
244
|
+
patch_entity(entity.resource_name, name, patch, 'strategic-merge-patch', namespace)
|
245
|
+
end
|
246
|
+
|
247
|
+
define_singleton_method("json_patch_#{entity.method_names[0]}") \
|
248
|
+
do |name, patch, namespace = nil|
|
249
|
+
patch_entity(entity.resource_name, name, patch, 'json-patch', namespace)
|
250
|
+
end
|
251
|
+
|
252
|
+
define_singleton_method("merge_patch_#{entity.method_names[0]}") \
|
253
|
+
do |name, patch, namespace = nil|
|
254
|
+
patch_entity(entity.resource_name, name, patch, 'merge-patch', namespace)
|
243
255
|
end
|
244
256
|
end
|
245
257
|
end
|
258
|
+
# rubocop:enable Metrics/BlockLength
|
246
259
|
|
247
260
|
def self.underscore_entity(entity_name)
|
248
261
|
entity_name.gsub(/([a-z])([A-Z])/, '\1_\2').downcase
|
@@ -381,13 +394,13 @@ module Kubeclient
|
|
381
394
|
format_response(@as, response.body)
|
382
395
|
end
|
383
396
|
|
384
|
-
def patch_entity(resource_name, name, patch, namespace
|
397
|
+
def patch_entity(resource_name, name, patch, strategy, namespace)
|
385
398
|
ns_prefix = build_namespace_prefix(namespace)
|
386
399
|
response = handle_exception do
|
387
400
|
rest_client[ns_prefix + resource_name + "/#{name}"]
|
388
401
|
.patch(
|
389
402
|
patch.to_json,
|
390
|
-
{ 'Content-Type' =>
|
403
|
+
{ 'Content-Type' => "application/#{strategy}+json" }.merge(@headers)
|
391
404
|
)
|
392
405
|
end
|
393
406
|
format_response(@as, response.body)
|
data/lib/kubeclient/config.rb
CHANGED
@@ -153,6 +153,14 @@ module Kubeclient
|
|
153
153
|
exec_opts = user['exec'].dup
|
154
154
|
exec_opts['command'] = ext_command_path(exec_opts['command']) if exec_opts['command']
|
155
155
|
options[:bearer_token] = Kubeclient::ExecCredentials.token(exec_opts)
|
156
|
+
elsif user.key?('auth-provider')
|
157
|
+
auth_provider = user['auth-provider']
|
158
|
+
options[:bearer_token] = case auth_provider['name']
|
159
|
+
when 'gcp'
|
160
|
+
then Kubeclient::GoogleApplicationDefaultCredentials.token
|
161
|
+
when 'oidc'
|
162
|
+
then Kubeclient::OIDCAuthProvider.token(auth_provider['config'])
|
163
|
+
end
|
156
164
|
else
|
157
165
|
%w[username password].each do |attr|
|
158
166
|
options[attr.to_sym] = user[attr] if user.key?(attr)
|
@@ -3,9 +3,19 @@
|
|
3
3
|
module Kubeclient
|
4
4
|
# Get a bearer token from the Google's application default credentials.
|
5
5
|
class GoogleApplicationDefaultCredentials
|
6
|
+
class GoogleDependencyError < LoadError # rubocop:disable Lint/InheritException
|
7
|
+
end
|
8
|
+
|
6
9
|
class << self
|
7
10
|
def token
|
8
|
-
|
11
|
+
begin
|
12
|
+
require 'googleauth'
|
13
|
+
rescue LoadError => e
|
14
|
+
raise GoogleDependencyError,
|
15
|
+
'Error requiring googleauth gem. Kubeclient itself does not include the ' \
|
16
|
+
'googleauth gem. To support auth-provider gcp, you must include it in your ' \
|
17
|
+
"calling application. Failed with: #{e.message}"
|
18
|
+
end
|
9
19
|
scopes = ['https://www.googleapis.com/auth/cloud-platform']
|
10
20
|
authorization = Google::Auth.get_application_default(scopes)
|
11
21
|
authorization.apply({})
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kubeclient
|
4
|
+
# Uses OIDC id-tokens and refreshes them if they are stale.
|
5
|
+
class OIDCAuthProvider
|
6
|
+
class OpenIDConnectDependencyError < LoadError # rubocop:disable Lint/InheritException
|
7
|
+
end
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def token(provider_config)
|
11
|
+
begin
|
12
|
+
require 'openid_connect'
|
13
|
+
rescue LoadError => e
|
14
|
+
raise OpenIDConnectDependencyError,
|
15
|
+
'Error requiring openid_connect gem. Kubeclient itself does not include the ' \
|
16
|
+
'openid_connect gem. To support auth-provider oidc, you must include it in your ' \
|
17
|
+
"calling application. Failed with: #{e.message}"
|
18
|
+
end
|
19
|
+
|
20
|
+
issuer_url = provider_config['idp-issuer-url']
|
21
|
+
discovery = OpenIDConnect::Discovery::Provider::Config.discover! issuer_url
|
22
|
+
|
23
|
+
if provider_config.key? 'id-token'
|
24
|
+
id_token = OpenIDConnect::ResponseObject::IdToken.decode provider_config['id-token'],
|
25
|
+
discovery.jwks
|
26
|
+
return provider_config['id-token'] unless expired?(id_token)
|
27
|
+
end
|
28
|
+
|
29
|
+
client = OpenIDConnect::Client.new(
|
30
|
+
identifier: provider_config['client-id'],
|
31
|
+
secret: provider_config['client-secret'],
|
32
|
+
authorization_endpoint: discovery.authorization_endpoint,
|
33
|
+
token_endpoint: discovery.token_endpoint,
|
34
|
+
userinfo_endpoint: discovery.userinfo_endpoint
|
35
|
+
)
|
36
|
+
client.refresh_token = provider_config['refresh-token']
|
37
|
+
client.access_token!.id_token
|
38
|
+
end
|
39
|
+
|
40
|
+
def expired?(id_token)
|
41
|
+
# If token expired or expiring within 60 seconds
|
42
|
+
Time.now.to_i + 60 > id_token.exp.to_i
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
data/lib/kubeclient/version.rb
CHANGED
@@ -0,0 +1,22 @@
|
|
1
|
+
apiVersion: v1
|
2
|
+
clusters:
|
3
|
+
- cluster:
|
4
|
+
server: https://localhost:8443
|
5
|
+
insecure-skip-tls-verify: true
|
6
|
+
name: localhost:8443
|
7
|
+
contexts:
|
8
|
+
- context:
|
9
|
+
cluster: localhost:8443
|
10
|
+
namespace: default
|
11
|
+
user: application-default-credentials
|
12
|
+
name: localhost/application-default-credentials
|
13
|
+
kind: Config
|
14
|
+
preferences: {}
|
15
|
+
users:
|
16
|
+
- name: application-default-credentials
|
17
|
+
user:
|
18
|
+
auth-provider:
|
19
|
+
config:
|
20
|
+
access-token: <fake_token>
|
21
|
+
expiry: 2019-02-19T11:07:29.827352-05:00
|
22
|
+
name: gcp
|
@@ -0,0 +1,25 @@
|
|
1
|
+
apiVersion: v1
|
2
|
+
clusters:
|
3
|
+
- cluster:
|
4
|
+
server: https://localhost:8443
|
5
|
+
insecure-skip-tls-verify: true
|
6
|
+
name: localhost:8443
|
7
|
+
contexts:
|
8
|
+
- context:
|
9
|
+
cluster: localhost:8443
|
10
|
+
namespace: default
|
11
|
+
user: oidc-auth-provider
|
12
|
+
name: localhost/oidc-auth-provider
|
13
|
+
kind: Config
|
14
|
+
preferences: {}
|
15
|
+
users:
|
16
|
+
- name: oidc-auth-provider
|
17
|
+
user:
|
18
|
+
auth-provider:
|
19
|
+
config:
|
20
|
+
client-id: fake-client-id
|
21
|
+
client-secret: fake-client-secret
|
22
|
+
id-token: fake-id-token
|
23
|
+
idp-issuer-url: https://accounts.google.com
|
24
|
+
refresh-token: fake-refresh-token
|
25
|
+
name: oidc
|
@@ -0,0 +1,26 @@
|
|
1
|
+
{
|
2
|
+
"status" : {},
|
3
|
+
"kind" : "Service",
|
4
|
+
"apiVersion" : "v1",
|
5
|
+
"spec" : {
|
6
|
+
"ports" : [
|
7
|
+
{
|
8
|
+
"targetPort" : 80,
|
9
|
+
"nodePort" : 0,
|
10
|
+
"port" : 80,
|
11
|
+
"protocol" : "TCP"
|
12
|
+
}
|
13
|
+
],
|
14
|
+
"clusterIP" : "1.2.3.4",
|
15
|
+
"type": "LoadBalancer"
|
16
|
+
},
|
17
|
+
"metadata" : {
|
18
|
+
"name" : "my-service",
|
19
|
+
"creationTimestamp" : null,
|
20
|
+
"namespace" : "development",
|
21
|
+
"resourceVersion" : "2",
|
22
|
+
"annotations" : {
|
23
|
+
"key" : "value"
|
24
|
+
}
|
25
|
+
}
|
26
|
+
}
|
@@ -0,0 +1,26 @@
|
|
1
|
+
{
|
2
|
+
"status" : {},
|
3
|
+
"kind" : "Service",
|
4
|
+
"apiVersion" : "v1",
|
5
|
+
"spec" : {
|
6
|
+
"ports" : [
|
7
|
+
{
|
8
|
+
"targetPort" : 80,
|
9
|
+
"nodePort" : 0,
|
10
|
+
"port" : 80,
|
11
|
+
"protocol" : "TCP"
|
12
|
+
}
|
13
|
+
],
|
14
|
+
"clusterIP" : "1.2.3.4",
|
15
|
+
"type": "NodePort"
|
16
|
+
},
|
17
|
+
"metadata" : {
|
18
|
+
"name" : "my-service",
|
19
|
+
"creationTimestamp" : null,
|
20
|
+
"namespace" : "development",
|
21
|
+
"resourceVersion" : "2",
|
22
|
+
"annotations" : {
|
23
|
+
"key" : "value"
|
24
|
+
}
|
25
|
+
}
|
26
|
+
}
|
data/test/test_config.rb
CHANGED
@@ -123,6 +123,43 @@ class KubeclientConfigTest < MiniTest::Test
|
|
123
123
|
end
|
124
124
|
end
|
125
125
|
|
126
|
+
def test_gcp_default_auth
|
127
|
+
Kubeclient::GoogleApplicationDefaultCredentials.expects(:token).returns('token1').once
|
128
|
+
parsed = YAML.safe_load(File.read(config_file('gcpauth.kubeconfig')), [Date, Time])
|
129
|
+
config = Kubeclient::Config.new(parsed, nil)
|
130
|
+
config.context(config.contexts.first)
|
131
|
+
end
|
132
|
+
|
133
|
+
# Each call to .context() obtains a new token, calling .auth_options doesn't change anything.
|
134
|
+
# NOTE: this is not a guarantee, may change, just testing current behavior.
|
135
|
+
def test_gcp_default_auth_renew
|
136
|
+
Kubeclient::GoogleApplicationDefaultCredentials.expects(:token).returns('token1').once
|
137
|
+
parsed = YAML.safe_load(File.read(config_file('gcpauth.kubeconfig')), [Date, Time])
|
138
|
+
config = Kubeclient::Config.new(parsed, nil)
|
139
|
+
context = config.context(config.contexts.first)
|
140
|
+
assert_equal({ bearer_token: 'token1' }, context.auth_options)
|
141
|
+
assert_equal({ bearer_token: 'token1' }, context.auth_options)
|
142
|
+
|
143
|
+
Kubeclient::GoogleApplicationDefaultCredentials.expects(:token).returns('token2').once
|
144
|
+
context2 = config.context(config.contexts.first)
|
145
|
+
assert_equal({ bearer_token: 'token2' }, context2.auth_options)
|
146
|
+
assert_equal({ bearer_token: 'token1' }, context.auth_options)
|
147
|
+
end
|
148
|
+
|
149
|
+
def test_oidc_auth_provider
|
150
|
+
Kubeclient::OIDCAuthProvider.expects(:token)
|
151
|
+
.with('client-id' => 'fake-client-id',
|
152
|
+
'client-secret' => 'fake-client-secret',
|
153
|
+
'id-token' => 'fake-id-token',
|
154
|
+
'idp-issuer-url' => 'https://accounts.google.com',
|
155
|
+
'refresh-token' => 'fake-refresh-token')
|
156
|
+
.returns('token1')
|
157
|
+
.once
|
158
|
+
parsed = YAML.safe_load(File.read(config_file('oidcauth.kubeconfig')))
|
159
|
+
config = Kubeclient::Config.new(parsed, nil)
|
160
|
+
config.context(config.contexts.first)
|
161
|
+
end
|
162
|
+
|
126
163
|
private
|
127
164
|
|
128
165
|
def check_context(context, ssl: true)
|
data/test/test_helper.rb
CHANGED
@@ -0,0 +1,84 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
require 'openid_connect'
|
3
|
+
|
4
|
+
class OIDCAuthProviderTest < MiniTest::Test
|
5
|
+
def setup
|
6
|
+
@client_id = 'client_id'
|
7
|
+
@client_secret = 'client_secret'
|
8
|
+
@idp_issuer_url = 'idp_issuer_url'
|
9
|
+
@refresh_token = 'refresh_token'
|
10
|
+
@id_token = 'id_token'
|
11
|
+
@new_id_token = 'new_id_token'
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_expired_token
|
15
|
+
OpenIDConnect::Discovery::Provider::Config.stub(:discover!, discovery_mock) do
|
16
|
+
OpenIDConnect::ResponseObject::IdToken.stub(:decode, id_token_mock(Time.now.to_i - 7200)) do
|
17
|
+
OpenIDConnect::Client.stub(:new, openid_client_mock) do
|
18
|
+
retrieved_id_token = Kubeclient::OIDCAuthProvider.token(
|
19
|
+
'client-id' => @client_id,
|
20
|
+
'client-secret' => @client_secret,
|
21
|
+
'id-token' => @id_token,
|
22
|
+
'idp-issuer-url' => @idp_issuer_url,
|
23
|
+
'refresh-token' => @refresh_token
|
24
|
+
)
|
25
|
+
assert_equal(@new_id_token, retrieved_id_token)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_valid_token
|
32
|
+
OpenIDConnect::Discovery::Provider::Config.stub(:discover!, discovery_mock) do
|
33
|
+
OpenIDConnect::ResponseObject::IdToken.stub(:decode, id_token_mock(Time.now.to_i + 7200)) do
|
34
|
+
retrieved_id_token = Kubeclient::OIDCAuthProvider.token(
|
35
|
+
'client-id' => @client_id,
|
36
|
+
'client-secret' => @client_secret,
|
37
|
+
'id-token' => @id_token,
|
38
|
+
'idp-issuer-url' => @idp_issuer_url,
|
39
|
+
'refresh-token' => @refresh_token
|
40
|
+
)
|
41
|
+
assert_equal(@id_token, retrieved_id_token)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_missing_id_token
|
47
|
+
OpenIDConnect::Discovery::Provider::Config.stub(:discover!, discovery_mock) do
|
48
|
+
OpenIDConnect::Client.stub(:new, openid_client_mock) do
|
49
|
+
retrieved_id_token = Kubeclient::OIDCAuthProvider.token(
|
50
|
+
'client-id' => @client_id,
|
51
|
+
'client-secret' => @client_secret,
|
52
|
+
'idp-issuer-url' => @idp_issuer_url,
|
53
|
+
'refresh-token' => @refresh_token
|
54
|
+
)
|
55
|
+
assert_equal(@new_id_token, retrieved_id_token)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def openid_client_mock
|
63
|
+
access_token = Minitest::Mock.new
|
64
|
+
access_token.expect(@id_token, @new_id_token)
|
65
|
+
|
66
|
+
openid_client = Minitest::Mock.new
|
67
|
+
openid_client.expect(:refresh_token=, nil, [@refresh_token])
|
68
|
+
openid_client.expect(:access_token!, access_token)
|
69
|
+
end
|
70
|
+
|
71
|
+
def id_token_mock(expiry)
|
72
|
+
id_token_mock = Minitest::Mock.new
|
73
|
+
id_token_mock.expect(:exp, expiry)
|
74
|
+
end
|
75
|
+
|
76
|
+
def discovery_mock
|
77
|
+
discovery = Minitest::Mock.new
|
78
|
+
discovery.expect(:jwks, 'jwks')
|
79
|
+
discovery.expect(:authorization_endpoint, 'authz_endpoint')
|
80
|
+
discovery.expect(:token_endpoint, 'token_endpoint')
|
81
|
+
discovery.expect(:userinfo_endpoint, 'userinfo_endpoint')
|
82
|
+
discovery
|
83
|
+
end
|
84
|
+
end
|
data/test/test_service.rb
CHANGED
@@ -273,4 +273,58 @@ class TestService < MiniTest::Test
|
|
273
273
|
data['metadata']['annotations']['key'] == 'value'
|
274
274
|
end
|
275
275
|
end
|
276
|
+
|
277
|
+
def test_json_patch_service
|
278
|
+
service = Kubeclient::Resource.new
|
279
|
+
name = 'my-service'
|
280
|
+
|
281
|
+
service.metadata = {}
|
282
|
+
service.metadata.name = name
|
283
|
+
service.metadata.namespace = 'development'
|
284
|
+
|
285
|
+
stub_core_api_list
|
286
|
+
expected_url = "http://localhost:8080/api/v1/namespaces/development/services/#{name}"
|
287
|
+
stub_request(:patch, expected_url)
|
288
|
+
.to_return(body: open_test_file('service_json_patch.json'), status: 200)
|
289
|
+
|
290
|
+
patch = [
|
291
|
+
{ 'op' => 'add', 'path' => '/spec/type', 'value' => 'LoadBalancer' }
|
292
|
+
]
|
293
|
+
|
294
|
+
client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
|
295
|
+
service = client.json_patch_service(name, patch, 'development')
|
296
|
+
assert_kind_of(RecursiveOpenStruct, service)
|
297
|
+
|
298
|
+
assert_requested(:patch, expected_url, times: 1) do |req|
|
299
|
+
data = JSON.parse(req.body)
|
300
|
+
req.headers['Content-Type'] == 'application/json-patch+json' &&
|
301
|
+
data == patch
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
def test_merge_patch_service
|
306
|
+
service = Kubeclient::Resource.new
|
307
|
+
name = 'my-service'
|
308
|
+
|
309
|
+
service.metadata = {}
|
310
|
+
service.metadata.name = name
|
311
|
+
service.metadata.namespace = 'development'
|
312
|
+
|
313
|
+
stub_core_api_list
|
314
|
+
expected_url = "http://localhost:8080/api/v1/namespaces/development/services/#{name}"
|
315
|
+
stub_request(:patch, expected_url)
|
316
|
+
.to_return(body: open_test_file('service_merge_patch.json'), status: 200)
|
317
|
+
|
318
|
+
patch = { spec: { type: 'NodePort' } }
|
319
|
+
|
320
|
+
client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
|
321
|
+
service = client.merge_patch_service(name, patch, 'development')
|
322
|
+
assert_kind_of(RecursiveOpenStruct, service)
|
323
|
+
|
324
|
+
assert_requested(:patch, expected_url, times: 1) do |req|
|
325
|
+
data = JSON.parse(req.body)
|
326
|
+
req.headers['Content-Type'] == 'application/merge-patch+json' &&
|
327
|
+
data['spec']['type'] == 'NodePort'
|
328
|
+
end
|
329
|
+
end
|
276
330
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kubeclient
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.
|
4
|
+
version: 4.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alissa Bonas
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-03-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -122,6 +122,34 @@ dependencies:
|
|
122
122
|
- - "~>"
|
123
123
|
- !ruby/object:Gem::Version
|
124
124
|
version: 0.5.1
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: mocha
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '1.5'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '1.5'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: openid_connect
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - "~>"
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '1.1'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - "~>"
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '1.1'
|
125
153
|
- !ruby/object:Gem::Dependency
|
126
154
|
name: rest-client
|
127
155
|
requirement: !ruby/object:Gem::Requirement
|
@@ -195,6 +223,7 @@ files:
|
|
195
223
|
- lib/kubeclient/google_application_default_credentials.rb
|
196
224
|
- lib/kubeclient/http_error.rb
|
197
225
|
- lib/kubeclient/missing_kind_compatibility.rb
|
226
|
+
- lib/kubeclient/oidc_auth_provider.rb
|
198
227
|
- lib/kubeclient/resource.rb
|
199
228
|
- lib/kubeclient/resource_not_found_error.rb
|
200
229
|
- lib/kubeclient/version.rb
|
@@ -206,7 +235,9 @@ files:
|
|
206
235
|
- test/config/external-cert.pem
|
207
236
|
- test/config/external-key.rsa
|
208
237
|
- test/config/external.kubeconfig
|
238
|
+
- test/config/gcpauth.kubeconfig
|
209
239
|
- test/config/nouser.kubeconfig
|
240
|
+
- test/config/oidcauth.kubeconfig
|
210
241
|
- test/config/timestamps.kubeconfig
|
211
242
|
- test/config/userauth.kubeconfig
|
212
243
|
- test/json/bindings_list.json
|
@@ -258,7 +289,9 @@ files:
|
|
258
289
|
- test/json/service_account.json
|
259
290
|
- test/json/service_account_list.json
|
260
291
|
- test/json/service_illegal_json_404.json
|
292
|
+
- test/json/service_json_patch.json
|
261
293
|
- test/json/service_list.json
|
294
|
+
- test/json/service_merge_patch.json
|
262
295
|
- test/json/service_patch.json
|
263
296
|
- test/json/service_update.json
|
264
297
|
- test/json/template.json
|
@@ -279,6 +312,7 @@ files:
|
|
279
312
|
- test/test_missing_methods.rb
|
280
313
|
- test/test_namespace.rb
|
281
314
|
- test/test_node.rb
|
315
|
+
- test/test_oidc_auth_provider.rb
|
282
316
|
- test/test_persistent_volume.rb
|
283
317
|
- test/test_persistent_volume_claim.rb
|
284
318
|
- test/test_pod.rb
|
@@ -326,7 +360,9 @@ test_files:
|
|
326
360
|
- test/config/external-cert.pem
|
327
361
|
- test/config/external-key.rsa
|
328
362
|
- test/config/external.kubeconfig
|
363
|
+
- test/config/gcpauth.kubeconfig
|
329
364
|
- test/config/nouser.kubeconfig
|
365
|
+
- test/config/oidcauth.kubeconfig
|
330
366
|
- test/config/timestamps.kubeconfig
|
331
367
|
- test/config/userauth.kubeconfig
|
332
368
|
- test/json/bindings_list.json
|
@@ -378,7 +414,9 @@ test_files:
|
|
378
414
|
- test/json/service_account.json
|
379
415
|
- test/json/service_account_list.json
|
380
416
|
- test/json/service_illegal_json_404.json
|
417
|
+
- test/json/service_json_patch.json
|
381
418
|
- test/json/service_list.json
|
419
|
+
- test/json/service_merge_patch.json
|
382
420
|
- test/json/service_patch.json
|
383
421
|
- test/json/service_update.json
|
384
422
|
- test/json/template.json
|
@@ -399,6 +437,7 @@ test_files:
|
|
399
437
|
- test/test_missing_methods.rb
|
400
438
|
- test/test_namespace.rb
|
401
439
|
- test/test_node.rb
|
440
|
+
- test/test_oidc_auth_provider.rb
|
402
441
|
- test/test_persistent_volume.rb
|
403
442
|
- test/test_persistent_volume_claim.rb
|
404
443
|
- test/test_pod.rb
|