Skip to content

Commit e05c715

Browse files
authored
Merge pull request #552 from cben/v4.y-openssl-x509-store-add-file
[v4.y] Load cluster ca certificates using OpenSSL::X509::Store#add_file
2 parents b6d9098 + b1824ed commit e05c715

File tree

10 files changed

+203
-31
lines changed

10 files changed

+203
-31
lines changed

.github/workflows/actions.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ on:
1010
- '**'
1111
jobs:
1212
build:
13+
continue-on-error: true
1314
runs-on: ${{ matrix.os_and_command.os }}
1415
strategy:
1516
matrix:

CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,21 @@ Notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
55
Kubeclient release versioning follows [SemVer](https://semver.org/).
66

7+
## Unreleased 4.9.z
8+
9+
### Fixed
10+
11+
- `Config`: fixed parsing of `certificate-authority` file containing concatenation of
12+
several certificates. Previously, server's cert was checked against only first CA cert,
13+
resulting in possible "certificate verify failed" errors.
14+
15+
An important use case is a chain of root & intermediate cert(s) - necessary when cluster's CA
16+
itself is signed by another custom CA.
17+
But also helps when you simply concatenate independent certs. (#461, #552)
18+
19+
- Still broken (#460): inline `certificate-authority-data` is still parsed using `add_cert`
20+
method that handles only one cert.
21+
722
## 4.9.2 — 2021-05-30
823

924
### Added

lib/kubeclient/config.rb

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,16 +51,15 @@ def context(context_name = nil)
5151
user['exec_result'] = ExecCredentials.run(exec_opts)
5252
end
5353

54-
ca_cert_data = fetch_cluster_ca_data(cluster)
5554
client_cert_data = fetch_user_cert_data(user)
5655
client_key_data = fetch_user_key_data(user)
5756
auth_options = fetch_user_auth_options(user)
5857

5958
ssl_options = {}
6059

61-
if !ca_cert_data.nil?
60+
if cluster_ca_data?(cluster)
6261
cert_store = OpenSSL::X509::Store.new
63-
cert_store.add_cert(OpenSSL::X509::Certificate.new(ca_cert_data))
62+
populate_cert_store_from_cluster_ca_data(cluster, cert_store)
6463
ssl_options[:verify_ssl] = OpenSSL::SSL::VERIFY_PEER
6564
ssl_options[:cert_store] = cert_store
6665
else
@@ -131,11 +130,16 @@ def fetch_context(context_name)
131130
[cluster, user, namespace]
132131
end
133132

134-
def fetch_cluster_ca_data(cluster)
133+
def cluster_ca_data?(cluster)
134+
cluster.key?('certificate-authority') || cluster.key?('certificate-authority-data')
135+
end
136+
137+
def populate_cert_store_from_cluster_ca_data(cluster, cert_store)
135138
if cluster.key?('certificate-authority')
136-
File.read(ext_file_path(cluster['certificate-authority']))
139+
cert_store.add_file(ext_file_path(cluster['certificate-authority']))
137140
elsif cluster.key?('certificate-authority-data')
138-
Base64.decode64(cluster['certificate-authority-data'])
141+
ca_cert_data = Base64.decode64(cluster['certificate-authority-data'])
142+
cert_store.add_cert(OpenSSL::X509::Certificate.new(ca_cert_data))
139143
end
140144
end
141145

test/config/another-ca1.pem

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIDADCCAeigAwIBAgIUQZjM/5qoAF78qIDyc+rKi4qBdOIwDQYJKoZIhvcNAQEL
3+
BQAwGDEWMBQGA1UEAxMNa3ViZXJuZXRlcy1jYTAeFw0yMjAzMjIxNDQzMDBaFw0z
4+
MjAzMTkxNDQzMDBaMBgxFjAUBgNVBAMTDWt1YmVybmV0ZXMtY2EwggEiMA0GCSqG
5+
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGkG7g+UjpDhZ7A4Pm7Hme+RWs5IHz4I2X
6+
IclvtO3LuJ26yzz2S8VaXFFeUqzEPb2G1RxFGvoAVN7qrTw0n5MQJCFLAA4dI7oY
7+
8XLRJ7KgTBBIw1jYpgKb2zyHPIJE6VmslliKUiX+QDovdRU/dsbdup2EucrnGw4+
8+
QNNAc3XMbXgm6lubA6znYZlSpcQ8BKer3tq75q4KUZicIjS6gKQyZjk9a6fcOuCS
9+
ybtlAKp9lYzcwxZkNrx+V1PJMQ1qaJWPnMAVi7Oj5Dm3Jmf1WHBcNEh52Q/0vYlt
10+
4WSaeM5t/Py/m/7c4Ve97f5m2X6EhYyUbzov4qeZOnIJI3MnU1FxAgMBAAGjQjBA
11+
MA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSl1qyt
12+
jd96WstRE8h9x5qkCvZUvjANBgkqhkiG9w0BAQsFAAOCAQEAJt55qYvBaniAwvgO
13+
tbO79g1FcQGrxpMX45TuoCE/K+MWDjrr6bp+FbLOqT8MwOsbGwwJIRTHGvkEkVso
14+
5AWI5aSNs3hWnltOdz27ZSHeX77WB4daK1tLK6ggZrp3v9iIpbBwWBFdmAqsPvEs
15+
H17K2BgAzdh6xRKPQd0BGTUpJBfk50R2gDMj7FKyIzBN69IOGytBfAXBhHzEGy4+
16+
MvtTEIMUjR//KgCrpNeyDuaWHttR5FdnuRxFO7O3BAfyNSaNmd/IEHQf7DIGgzOy
17+
+xWLyH/HRHj5C70qAqjbnrgBODI99BsA9U7oXTuyPLdIboAcFt2zD5DIYgZET52X
18+
53w4jA==
19+
-----END CERTIFICATE-----

test/config/another-ca2.pem

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIDADCCAeigAwIBAgIUHW3OPnmuTquJ0YgbGpmm/blsY2QwDQYJKoZIhvcNAQEL
3+
BQAwGDEWMBQGA1UEAxMNa3ViZXJuZXRlcy1jYTAeFw0yMjAzMjIxNDQ0MDBaFw0z
4+
MjAzMTkxNDQ0MDBaMBgxFjAUBgNVBAMTDWt1YmVybmV0ZXMtY2EwggEiMA0GCSqG
5+
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDLMEJs5agS0hNQBxPTtsI6dIhIi/pY8liI
6+
sNukbi5KwKf80FYNyRXqE8ufDVyTFzOc+MG96jnHjDaBWjrVN9On0PgUBo4nPyd4
7+
DtyvYx2jMzwToSEIo/Z1aroMx1oGywCgdS4/3FWAbhlSbyXKJmhfh6gX0TxWz+dV
8+
zqNuqQq9EWuRhOMg9vgzjfp3mjiPE10lW8pT0j5JT3PI/eGO+C2Z7z33LJXb6GM2
9+
nXvhGFMGY+7XG65pqJ3L8g1mk+LjPiwyIItw8wPtrnrZ2VXMklMd5Mn+jgCTNe1B
10+
om0nPpPIiTblCr6gcNcVjy5WGN37OKlqrT0JTuSPHcxSUp05LFjDAgMBAAGjQjBA
11+
MA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQvV/sB
12+
wbR3UwjkLAMN+6P3fZ/3OjANBgkqhkiG9w0BAQsFAAOCAQEACAk4EQwCkw2EBsSR
13+
2SKoa1SjYFkZzIr/0/TB2YcMUvHF+RpvlD5vQ8/RJjeAl1kc6/niZ9TWCemjBLqI
14+
hPoFe49zr49DyQjC2ZfsXVJvFCr6g7o4q4DtQ6ltyBuTJbkn1hI+aB8zgvpofG44
15+
mKj18Y7tPvgXtRua4SaeBq777+22AOvKxPied9p4PTrMN4RKTP6+yIbLflej7dBD
16+
zQDjfmmYsH0T2ZRtBpE1dYrUbU3tkizcMZRJBgreoxoff+r5coibMIm/7gh+YoSb
17+
BCItCaeuGSKQ8CJb8DElcPUd6nKUjmeiQL68ztsG/+CXLiL/TZb914VaaCXvPInw
18+
49jJ7w==
19+
-----END CERTIFICATE-----
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
apiVersion: v1
2+
clusters:
3+
- cluster:
4+
certificate-authority: concatenated-ca.pem
5+
server: https://localhost:6443
6+
name: local
7+
contexts:
8+
- context:
9+
cluster: local
10+
namespace: default
11+
user: user
12+
name: Default
13+
current-context: Default
14+
kind: Config
15+
preferences: {}
16+
users:
17+
- name: user
18+
user:
19+
client-certificate: external-cert.pem
20+
client-key: external-key.rsa

test/config/concatenated-ca.pem

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIDADCCAeigAwIBAgIUQZjM/5qoAF78qIDyc+rKi4qBdOIwDQYJKoZIhvcNAQEL
3+
BQAwGDEWMBQGA1UEAxMNa3ViZXJuZXRlcy1jYTAeFw0yMjAzMjIxNDQzMDBaFw0z
4+
MjAzMTkxNDQzMDBaMBgxFjAUBgNVBAMTDWt1YmVybmV0ZXMtY2EwggEiMA0GCSqG
5+
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGkG7g+UjpDhZ7A4Pm7Hme+RWs5IHz4I2X
6+
IclvtO3LuJ26yzz2S8VaXFFeUqzEPb2G1RxFGvoAVN7qrTw0n5MQJCFLAA4dI7oY
7+
8XLRJ7KgTBBIw1jYpgKb2zyHPIJE6VmslliKUiX+QDovdRU/dsbdup2EucrnGw4+
8+
QNNAc3XMbXgm6lubA6znYZlSpcQ8BKer3tq75q4KUZicIjS6gKQyZjk9a6fcOuCS
9+
ybtlAKp9lYzcwxZkNrx+V1PJMQ1qaJWPnMAVi7Oj5Dm3Jmf1WHBcNEh52Q/0vYlt
10+
4WSaeM5t/Py/m/7c4Ve97f5m2X6EhYyUbzov4qeZOnIJI3MnU1FxAgMBAAGjQjBA
11+
MA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSl1qyt
12+
jd96WstRE8h9x5qkCvZUvjANBgkqhkiG9w0BAQsFAAOCAQEAJt55qYvBaniAwvgO
13+
tbO79g1FcQGrxpMX45TuoCE/K+MWDjrr6bp+FbLOqT8MwOsbGwwJIRTHGvkEkVso
14+
5AWI5aSNs3hWnltOdz27ZSHeX77WB4daK1tLK6ggZrp3v9iIpbBwWBFdmAqsPvEs
15+
H17K2BgAzdh6xRKPQd0BGTUpJBfk50R2gDMj7FKyIzBN69IOGytBfAXBhHzEGy4+
16+
MvtTEIMUjR//KgCrpNeyDuaWHttR5FdnuRxFO7O3BAfyNSaNmd/IEHQf7DIGgzOy
17+
+xWLyH/HRHj5C70qAqjbnrgBODI99BsA9U7oXTuyPLdIboAcFt2zD5DIYgZET52X
18+
53w4jA==
19+
-----END CERTIFICATE-----
20+
-----BEGIN CERTIFICATE-----
21+
MIIC/zCCAeegAwIBAgITPbfpy29aBG67ChRdB6lJegTkmDANBgkqhkiG9w0BAQsF
22+
ADAYMRYwFAYDVQQDEw1rdWJlcm5ldGVzLWNhMB4XDTIyMDIyMTA5MDIwMFoXDTMy
23+
MDIxOTA5MDIwMFowGDEWMBQGA1UEAxMNa3ViZXJuZXRlcy1jYTCCASIwDQYJKoZI
24+
hvcNAQEBBQADggEPADCCAQoCggEBAMLjZ2cFBalzoimaEcpT9Jz2JmdgsHMOagVd
25+
It7OQpTwDZ3npIICVpguEh9xtovR8m8/HYM+/a4vMQHT+3p8HPjiDaRYGg7OZ9L+
26+
Fp/9zhBuiaIvg8Z+Bbys9Q9Uuj6VEwfFJBcNH6TmzdiDgQUs5/k+6/vtuJ4ys3sD
27+
KkAOxqPXDaBoANnLpIxdIMQDcWSLFA0wmFhdZJq3KEAoJpEL0WYo1ZRBV3iH77yf
28+
sDbN1OBu2vNnRZ+DrV0ZJ5ApmbFXPX8i4KJaW9lCB62FN0j5XsNDoyTeAVpesfNs
29+
zYufVpBdqNZFkOKg9diMuTMika2aYfDuiVzdebDgcp9aMloKtbECAwEAAaNCMEAw
30+
DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFBdOiygC
31+
LcuJrq8rNa1xADr5Sp7CMA0GCSqGSIb3DQEBCwUAA4IBAQDCy4IlhASh6Br5XEcI
32+
TpP5ThD1OyRzQnsPe6P1qgWP3kBXK/AcsSl+VGtaZp2oEhJoUnsz7kE8yW3gK+PA
33+
51zY4aHTiF9xkyd5zOCAGB+cfp9Ys+szWzyu0QQ9IBjJ4+eDjg7W0/S+BM2Qn1iL
34+
jTFIe2Bdf+Q/J24/q3ksTXK17UNun14vDRsJgsNcrFt/rumfHPx1ytwsiqKyEKV7
35+
kFxSwa3d8/AvhGgFpPmfRjU7gAJCFcHz501zhi2a6L5TYBTecVRbqZoeHiZ0YNWI
36+
is5g4VmVB+BxMAM2WEd29v4l/3oI1Pey9rvt7NJqSe1im9uqZgVDeg/vP8zKs/dF
37+
ZYw8
38+
-----END CERTIFICATE-----
39+
-----BEGIN CERTIFICATE-----
40+
MIIDADCCAeigAwIBAgIUHW3OPnmuTquJ0YgbGpmm/blsY2QwDQYJKoZIhvcNAQEL
41+
BQAwGDEWMBQGA1UEAxMNa3ViZXJuZXRlcy1jYTAeFw0yMjAzMjIxNDQ0MDBaFw0z
42+
MjAzMTkxNDQ0MDBaMBgxFjAUBgNVBAMTDWt1YmVybmV0ZXMtY2EwggEiMA0GCSqG
43+
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDLMEJs5agS0hNQBxPTtsI6dIhIi/pY8liI
44+
sNukbi5KwKf80FYNyRXqE8ufDVyTFzOc+MG96jnHjDaBWjrVN9On0PgUBo4nPyd4
45+
DtyvYx2jMzwToSEIo/Z1aroMx1oGywCgdS4/3FWAbhlSbyXKJmhfh6gX0TxWz+dV
46+
zqNuqQq9EWuRhOMg9vgzjfp3mjiPE10lW8pT0j5JT3PI/eGO+C2Z7z33LJXb6GM2
47+
nXvhGFMGY+7XG65pqJ3L8g1mk+LjPiwyIItw8wPtrnrZ2VXMklMd5Mn+jgCTNe1B
48+
om0nPpPIiTblCr6gcNcVjy5WGN37OKlqrT0JTuSPHcxSUp05LFjDAgMBAAGjQjBA
49+
MA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQvV/sB
50+
wbR3UwjkLAMN+6P3fZ/3OjANBgkqhkiG9w0BAQsFAAOCAQEACAk4EQwCkw2EBsSR
51+
2SKoa1SjYFkZzIr/0/TB2YcMUvHF+RpvlD5vQ8/RJjeAl1kc6/niZ9TWCemjBLqI
52+
hPoFe49zr49DyQjC2ZfsXVJvFCr6g7o4q4DtQ6ltyBuTJbkn1hI+aB8zgvpofG44
53+
mKj18Y7tPvgXtRua4SaeBq777+22AOvKxPied9p4PTrMN4RKTP6+yIbLflej7dBD
54+
zQDjfmmYsH0T2ZRtBpE1dYrUbU3tkizcMZRJBgreoxoff+r5coibMIm/7gh+YoSb
55+
BCItCaeuGSKQ8CJb8DElcPUd6nKUjmeiQL68ztsG/+CXLiL/TZb914VaaCXvPInw
56+
49jJ7w==
57+
-----END CERTIFICATE-----

test/config/update_certs_k0s.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ def sh!(*cmd)
3333
# The rest could easily be extracted from allinone.kubeconfig, but the test is more robust
3434
# if we don't reuse YAML and/or Kubeclient::Config parsing to construct test data.
3535
sh! "#{DOCKER} exec #{CONTAINER} cat /var/lib/k0s/pki/ca.crt > test/config/external-ca.pem"
36+
sh! 'cat test/config/another-ca1.pem test/config/external-ca.pem '\
37+
' test/config/another-ca2.pem > test/config/concatenated-ca.pem'
3638
sh! "#{DOCKER} exec #{CONTAINER} cat /var/lib/k0s/pki/admin.crt > test/config/external-cert.pem"
3739
sh! "#{DOCKER} exec #{CONTAINER} cat /var/lib/k0s/pki/admin.key > test/config/external-key.rsa"
3840

test/test_config.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,12 @@ def test_external_nopath_absolute
4444
end
4545
end
4646

47+
def test_concatenated_ca
48+
config = Kubeclient::Config.read(config_file('concatenated-ca.kubeconfig'))
49+
assert_equal(['Default'], config.contexts)
50+
check_context(config.context, ssl: true)
51+
end
52+
4753
def test_nouser
4854
config = Kubeclient::Config.read(config_file('nouser.kubeconfig'))
4955
assert_equal(['default/localhost:6443/nouser'], config.contexts)

test/test_real_cluster.rb

Lines changed: 54 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -16,59 +16,88 @@ def teardown
1616
WebMock.disable_net_connect! # Don't allow any connections in other tests.
1717
end
1818

19+
# Partially isolated tests that check Client behavior with given `verify_ssl` value:
20+
21+
# localhost and 127.0.0.1 are among names on the certificate
22+
HOSTNAME_COVERED_BY_CERT = 'https://127.0.0.1:6443'.freeze
23+
# 127.0.0.2 also means localhost but is not included in the certificate.
24+
HOSTNAME_NOT_ON_CERT = 'https://127.0.0.2:6443'.freeze
25+
1926
def test_real_cluster_verify_peer
2027
config = Kubeclient::Config.read(config_file('external.kubeconfig'))
2128
context = config.context
22-
# localhost and 127.0.0.1 are among names on the certificate
2329
client1 = Kubeclient::Client.new(
24-
'https://127.0.0.1:6443', 'v1',
30+
HOSTNAME_COVERED_BY_CERT, 'v1',
2531
ssl_options: context.ssl_options.merge(verify_ssl: OpenSSL::SSL::VERIFY_PEER),
2632
auth_options: context.auth_options
2733
)
28-
client1.discover
29-
client1.get_nodes
30-
exercise_watcher_with_timeout(client1.watch_nodes)
31-
# 127.0.0.2 also means localhost but is not included in the certificate.
34+
check_cert_accepted(client1)
3235
client2 = Kubeclient::Client.new(
33-
'https://127.0.0.2:6443', 'v1',
36+
HOSTNAME_NOT_ON_CERT, 'v1',
3437
ssl_options: context.ssl_options.merge(verify_ssl: OpenSSL::SSL::VERIFY_PEER),
3538
auth_options: context.auth_options
3639
)
37-
# TODO: all OpenSSL exceptions should be wrapped with Kubeclient error.
38-
assert_raises(Kubeclient::HttpError, OpenSSL::SSL::SSLError) do
39-
client2.discover
40-
end
41-
# Since discovery fails, methods like .get_nodes, .watch_nodes would all fail
42-
# on method_missing -> discover. Call lower-level methods to test actual connection.
43-
assert_raises(Kubeclient::HttpError, OpenSSL::SSL::SSLError) do
44-
client2.get_entities('Node', 'nodes', {})
45-
end
46-
assert_raises(Kubeclient::HttpError, OpenSSL::SSL::SSLError) do
47-
exercise_watcher_with_timeout(client2.watch_entities('nodes'))
48-
end
40+
check_cert_rejected(client2)
4941
end
5042

5143
def test_real_cluster_verify_none
5244
config = Kubeclient::Config.read(config_file('external.kubeconfig'))
5345
context = config.context
54-
# localhost and 127.0.0.1 are among names on the certificate
5546
client1 = Kubeclient::Client.new(
56-
'https://127.0.0.1:6443', 'v1',
47+
HOSTNAME_COVERED_BY_CERT, 'v1',
5748
ssl_options: context.ssl_options.merge(verify_ssl: OpenSSL::SSL::VERIFY_NONE),
5849
auth_options: context.auth_options
5950
)
60-
client1.get_nodes
61-
# 127.0.0.2 also means localhost but is not included in the certificate.
51+
check_cert_accepted(client1)
6252
client2 = Kubeclient::Client.new(
63-
'https://127.0.0.2:6443', 'v1',
53+
HOSTNAME_NOT_ON_CERT, 'v1',
6454
ssl_options: context.ssl_options.merge(verify_ssl: OpenSSL::SSL::VERIFY_NONE),
6555
auth_options: context.auth_options
6656
)
67-
client2.get_nodes
57+
check_cert_accepted(client2)
58+
end
59+
60+
def test_real_cluster_concatenated_ca
61+
config = Kubeclient::Config.read(config_file('concatenated-ca.kubeconfig'))
62+
context = config.context
63+
client1 = Kubeclient::Client.new(
64+
HOSTNAME_COVERED_BY_CERT, 'v1',
65+
ssl_options: context.ssl_options.merge(verify_ssl: OpenSSL::SSL::VERIFY_PEER),
66+
auth_options: context.auth_options
67+
)
68+
check_cert_accepted(client1)
69+
client2 = Kubeclient::Client.new(
70+
HOSTNAME_NOT_ON_CERT, 'v1',
71+
ssl_options: context.ssl_options.merge(verify_ssl: OpenSSL::SSL::VERIFY_PEER),
72+
auth_options: context.auth_options
73+
)
74+
check_cert_rejected(client2)
6875
end
6976

7077
private
7178

79+
# Test cert checking on discovery, CRUD, and watch code paths.
80+
def check_cert_accepted(client)
81+
client.discover
82+
client.get_nodes
83+
exercise_watcher_with_timeout(client.watch_nodes)
84+
end
85+
86+
def check_cert_rejected(client)
87+
# TODO: all OpenSSL exceptions should be wrapped with Kubeclient error.
88+
assert_raises(Kubeclient::HttpError, OpenSSL::SSL::SSLError) do
89+
client.discover
90+
end
91+
# Since discovery fails, methods like .get_nodes, .watch_nodes would all fail
92+
# on method_missing -> discover. Call lower-level methods to test actual connection.
93+
assert_raises(Kubeclient::HttpError, OpenSSL::SSL::SSLError) do
94+
client.get_entities('Node', 'nodes', {})
95+
end
96+
assert_raises(Kubeclient::HttpError, OpenSSL::SSL::SSLError) do
97+
exercise_watcher_with_timeout(client.watch_entities('nodes'))
98+
end
99+
end
100+
72101
def exercise_watcher_with_timeout(watcher)
73102
thread = Thread.new do
74103
sleep(1)

0 commit comments

Comments
 (0)