kubeclient 4.2.2 → 4.7.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.

@@ -1,4 +1,4 @@
1
1
  # Kubernetes REST-API Client
2
2
  module Kubeclient
3
- VERSION = '4.2.2'.freeze
3
+ VERSION = '4.7.0'.freeze
4
4
  end
@@ -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,26 @@
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
+ cmd-args: config config-helper --format=json
22
+ cmd-path: /path/to/gcloud
23
+ expiry: 2019-04-09T19:26:18Z
24
+ expiry-key: '{.credential.token_expiry}'
25
+ token-key: '{.credential.access_token}'
26
+ 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
+ }
@@ -123,6 +123,57 @@ 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_gcp_command_auth
150
+ Kubeclient::GCPCommandCredentials.expects(:token)
151
+ .with('access-token' => '<fake_token>',
152
+ 'cmd-args' => 'config config-helper --format=json',
153
+ 'cmd-path' => '/path/to/gcloud',
154
+ 'expiry' => '2019-04-09 19:26:18 UTC',
155
+ 'expiry-key' => '{.credential.token_expiry}',
156
+ 'token-key' => '{.credential.access_token}')
157
+ .returns('token1')
158
+ .once
159
+ config = Kubeclient::Config.read(config_file('gcpcmdauth.kubeconfig'))
160
+ config.context(config.contexts.first)
161
+ end
162
+
163
+ def test_oidc_auth_provider
164
+ Kubeclient::OIDCAuthProvider.expects(:token)
165
+ .with('client-id' => 'fake-client-id',
166
+ 'client-secret' => 'fake-client-secret',
167
+ 'id-token' => 'fake-id-token',
168
+ 'idp-issuer-url' => 'https://accounts.google.com',
169
+ 'refresh-token' => 'fake-refresh-token')
170
+ .returns('token1')
171
+ .once
172
+ parsed = YAML.safe_load(File.read(config_file('oidcauth.kubeconfig')))
173
+ config = Kubeclient::Config.new(parsed, nil)
174
+ config.context(config.contexts.first)
175
+ end
176
+
126
177
  private
127
178
 
128
179
  def check_context(context, ssl: true)
@@ -0,0 +1,27 @@
1
+ require_relative 'test_helper'
2
+ require 'open3'
3
+
4
+ # Unit tests for the GCPCommandCredentials token provider
5
+ class GCPCommandCredentialsTest < MiniTest::Test
6
+ def test_token
7
+ opts = { 'cmd-args' => 'config config-helper --format=json',
8
+ 'cmd-path' => '/path/to/gcloud',
9
+ 'expiry-key' => '{.credential.token_expiry}',
10
+ 'token-key' => '{.credential.access_token}' }
11
+
12
+ creds = JSON.dump(
13
+ 'credential' => {
14
+ 'access_token' => '9A3A941836F2458175BE18AA1971EBBF47949B07',
15
+ 'token_expiry' => '2019-04-12T15:02:51Z'
16
+ }
17
+ )
18
+
19
+ st = Minitest::Mock.new
20
+ st.expect(:success?, true)
21
+
22
+ Open3.stub(:capture3, [creds, nil, st]) do
23
+ assert_equal('9A3A941836F2458175BE18AA1971EBBF47949B07',
24
+ Kubeclient::GCPCommandCredentials.token(opts))
25
+ end
26
+ end
27
+ end
@@ -2,6 +2,7 @@ require 'bundler/setup'
2
2
  require 'minitest/autorun'
3
3
  require 'minitest/rg'
4
4
  require 'webmock/minitest'
5
+ require 'mocha/minitest'
5
6
  require 'json'
6
7
  require 'kubeclient'
7
8
 
@@ -296,6 +296,22 @@ class KubeclientTest < MiniTest::Test
296
296
  )
297
297
  end
298
298
 
299
+ def test_entities_with_resource_version
300
+ version = '329'
301
+
302
+ stub_core_api_list
303
+ stub_get_services
304
+
305
+ services = client.get_services(resource_version: version)
306
+
307
+ assert_instance_of(Kubeclient::Common::EntityList, services)
308
+ assert_requested(
309
+ :get,
310
+ "http://localhost:8080/api/v1/services?resourceVersion=#{version}",
311
+ times: 1
312
+ )
313
+ end
314
+
299
315
  def test_entities_with_field_selector
300
316
  selector = 'involvedObject.name=redis-master'
301
317
 
@@ -0,0 +1,103 @@
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
+ def test_token_with_unknown_kid
61
+ OpenIDConnect::Discovery::Provider::Config.stub(:discover!, discovery_mock) do
62
+ OpenIDConnect::ResponseObject::IdToken.stub(
63
+ :decode, ->(_token, _jwks) { raise JSON::JWK::Set::KidNotFound }
64
+ ) do
65
+ OpenIDConnect::Client.stub(:new, openid_client_mock) do
66
+ retrieved_id_token = Kubeclient::OIDCAuthProvider.token(
67
+ 'client-id' => @client_id,
68
+ 'client-secret' => @client_secret,
69
+ 'id-token' => @id_token,
70
+ 'idp-issuer-url' => @idp_issuer_url,
71
+ 'refresh-token' => @refresh_token
72
+ )
73
+ assert_equal(@new_id_token, retrieved_id_token)
74
+ end
75
+ end
76
+ end
77
+ end
78
+
79
+ private
80
+
81
+ def openid_client_mock
82
+ access_token = Minitest::Mock.new
83
+ access_token.expect(@id_token, @new_id_token)
84
+
85
+ openid_client = Minitest::Mock.new
86
+ openid_client.expect(:refresh_token=, nil, [@refresh_token])
87
+ openid_client.expect(:access_token!, access_token)
88
+ end
89
+
90
+ def id_token_mock(expiry)
91
+ id_token_mock = Minitest::Mock.new
92
+ id_token_mock.expect(:exp, expiry)
93
+ end
94
+
95
+ def discovery_mock
96
+ discovery = Minitest::Mock.new
97
+ discovery.expect(:jwks, 'jwks')
98
+ discovery.expect(:authorization_endpoint, 'authz_endpoint')
99
+ discovery.expect(:token_endpoint, 'token_endpoint')
100
+ discovery.expect(:userinfo_endpoint, 'userinfo_endpoint')
101
+ discovery
102
+ end
103
+ end
@@ -69,12 +69,31 @@ class TestPodLog < MiniTest::Test
69
69
  times: 1)
70
70
  end
71
71
 
72
+ def test_get_pod_limit_bytes
73
+ selected_bytes = open_test_file('pod_log.txt').read(10)
74
+
75
+ stub_request(:get, %r{/namespaces/default/pods/[a-z0-9-]+/log})
76
+ .to_return(body: selected_bytes,
77
+ status: 200)
78
+
79
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
80
+ retrieved_log = client.get_pod_log('redis-master-pod',
81
+ 'default',
82
+ limit_bytes: 10)
83
+
84
+ assert_equal(selected_bytes, retrieved_log)
85
+
86
+ assert_requested(:get,
87
+ 'http://localhost:8080/api/v1/namespaces/default/pods/redis-master-pod/log?limitBytes=10',
88
+ times: 1)
89
+ end
90
+
72
91
  def test_watch_pod_log
73
- expected_lines = open_test_file('pod_log.txt').read.split("\n")
92
+ file = open_test_file('pod_log.txt')
93
+ expected_lines = file.read.split("\n")
74
94
 
75
95
  stub_request(:get, %r{/namespaces/default/pods/[a-z0-9-]+/log\?.*follow})
76
- .to_return(body: open_test_file('pod_log.txt'),
77
- status: 200)
96
+ .to_return(body: file, status: 200)
78
97
 
79
98
  client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
80
99
 
@@ -85,6 +104,21 @@ class TestPodLog < MiniTest::Test
85
104
  end
86
105
  end
87
106
 
107
+ def test_watch_pod_log_with_block
108
+ file = open_test_file('pod_log.txt')
109
+ first = file.readlines.first.chomp
110
+
111
+ stub_request(:get, %r{/namespaces/default/pods/[a-z0-9-]+/log\?.*follow})
112
+ .to_return(body: file, status: 200)
113
+
114
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
115
+
116
+ client.watch_pod_log('redis-master-pod', 'default') do |line|
117
+ assert_equal first, line
118
+ break
119
+ end
120
+ end
121
+
88
122
  def test_watch_pod_log_follow_redirect
89
123
  expected_lines = open_test_file('pod_log.txt').read.split("\n")
90
124
  redirect = 'http://localhost:1234/api/namespaces/default/pods/redis-master-pod/log'
@@ -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